Skip to content

Commit

Permalink
Adds a way to explicitly specify the order of relationships in dynami…
Browse files Browse the repository at this point in the history
…c views.
  • Loading branch information
simonbrowndotje committed Jul 20, 2024
1 parent 53519eb commit ca88ead
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 15 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "<identifier> -> <identifier> [description] [technology]";
private static final String GRAMMAR_2 = "<identifier> [description]";
private static final String GRAMMAR_1 = "[order:] <identifier> -> <identifier> [description] [technology]";
private static final String GRAMMAR_2 = "[order:] <identifier> [description]";

private static final String ORDER_DELIMITER = ":";

private static final int SOURCE_IDENTIFIER_INDEX = 0;
private static final int RELATIONSHIP_TOKEN_INDEX = 1;
Expand All @@ -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))) {
// <element identifier> -> <element identifier> [description] [technology]
Expand Down Expand Up @@ -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 {
// <relationship identifier> [description] [technology]
// [order] <relationship identifier> [description] [technology]
String relationshipId = tokens.get(RELATIONSHIP_IDENTIFIER_INDEX);
Relationship relationship = context.getRelationship(relationshipId);

Expand All @@ -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");
Expand Down
4 changes: 4 additions & 0 deletions structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
16 changes: 12 additions & 4 deletions structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;

Expand Down Expand Up @@ -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<RelationshipView> relationships = view.getRelationships();
Iterator<RelationshipView> it = relationships.iterator();
assertEquals("2", it.next().getOrder());
assertEquals("3", it.next().getOrder());
}

@Test
void test_workspaceProperties() throws Exception {
StructurizrDslParser parser = new StructurizrDslParser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: <identifier> -> <identifier> [description] [technology]", e.getMessage());
assertEquals("Too many tokens, expected: [order:] <identifier> -> <identifier> [description] [technology]", e.getMessage());
}
}

Expand All @@ -29,7 +29,7 @@ void test_parseRelationship_ThrowsAnException_WhenTheDestinationIdentifierIsMiss
parser.parseRelationship(new DynamicViewDslContext(null), tokens("source", "->"));
fail();
} catch (Exception e) {
assertEquals("Expected: <identifier> -> <identifier> [description] [technology]", e.getMessage());
assertEquals("Expected: [order:] <identifier> -> <identifier> [description] [technology]", e.getMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}

}

0 comments on commit ca88ead

Please sign in to comment.