From 2fcd0eeec4731b9aec00a0d4735413eff1eebb61 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Nov 2023 14:16:03 +0000 Subject: [PATCH] Fixes #359. --- docs/changelog.md | 1 + .../dsl/DynamicViewContentParser.java | 15 +++--- .../dsl/DynamicViewRelationshipContext.java | 26 ++++++++++ .../dsl/DynamicViewRelationshipParser.java | 21 ++++++++ .../structurizr/dsl/StructurizrDslParser.java | 9 +++- src/test/dsl/test.dsl | 4 ++ .../DynamicViewRelationshipParserTests.java | 52 +++++++++++++++++++ 7 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/structurizr/dsl/DynamicViewRelationshipContext.java create mode 100644 src/main/java/com/structurizr/dsl/DynamicViewRelationshipParser.java create mode 100644 src/test/java/com/structurizr/dsl/DynamicViewRelationshipParserTests.java diff --git a/docs/changelog.md b/docs/changelog.md index 6dfd672..e71af92 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.34.0 (unreleased) - Fixes https://github.com/structurizr/dsl/issues/364 (.DS_Store file causes exception during !include on Windows). +- Fixes https://github.com/structurizr/dsl/issues/359 (Add url for relationship in dynamic view). - Adds a `getDslParser()` method to the `StructurizrDslPluginContext` class (https://github.com/structurizr/dsl/issues/361). - Adds the ability to specify the workspace scope via a `scope` keyword inside the workspace `configuration`. - Adds support for specifying perspective values. diff --git a/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java b/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java index 9412ba6..e96f6ba 100644 --- a/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java +++ b/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java @@ -5,6 +5,7 @@ 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 { @@ -19,7 +20,7 @@ final class DynamicViewContentParser extends AbstractParser { private static final int RELATIONSHIP_IDENTIFIER_INDEX = 0; - void parseRelationship(DynamicViewDslContext context, Tokens tokens) { + RelationshipView parseRelationship(DynamicViewDslContext context, Tokens tokens) { DynamicView view = context.getView(); if (tokens.size() > 1 && StructurizrDslTokens.RELATIONSHIP_TOKEN.equals(tokens.get(RELATIONSHIP_TOKEN_INDEX))) { @@ -64,13 +65,13 @@ void parseRelationship(DynamicViewDslContext context, Tokens tokens) { } if (sourceElement instanceof StaticStructureElement && destinationElement instanceof StaticStructureElement) { - view.add((StaticStructureElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); + return view.add((StaticStructureElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); } else if (sourceElement instanceof StaticStructureElement && destinationElement instanceof CustomElement) { - view.add((StaticStructureElement) sourceElement, description, technology, (CustomElement) destinationElement); + return view.add((StaticStructureElement) sourceElement, description, technology, (CustomElement) destinationElement); } else if (sourceElement instanceof CustomElement && destinationElement instanceof StaticStructureElement) { - view.add((CustomElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); + return view.add((CustomElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); } else if (sourceElement instanceof CustomElement && destinationElement instanceof CustomElement) { - view.add((CustomElement) sourceElement, description, technology, (CustomElement) destinationElement); + return view.add((CustomElement) sourceElement, description, technology, (CustomElement) destinationElement); } } else { // [description] [technology] @@ -90,8 +91,10 @@ void parseRelationship(DynamicViewDslContext context, Tokens tokens) { description = tokens.get(RELATIONSHIP_IDENTIFIER_INDEX+1); } - view.add(relationship, description); + return view.add(relationship, description); } + + throw new RuntimeException("The specified relationship could not be added"); } } \ No newline at end of file diff --git a/src/main/java/com/structurizr/dsl/DynamicViewRelationshipContext.java b/src/main/java/com/structurizr/dsl/DynamicViewRelationshipContext.java new file mode 100644 index 0000000..910ef61 --- /dev/null +++ b/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/src/main/java/com/structurizr/dsl/DynamicViewRelationshipParser.java b/src/main/java/com/structurizr/dsl/DynamicViewRelationshipParser.java new file mode 100644 index 0000000..5562e4d --- /dev/null +++ b/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/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index e88e91b..17fbedb 100644 --- a/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -793,12 +793,19 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio new ImageViewContentParser(restricted).parseImage(getContext(ImageViewDslContext.class), dslFile, tokens); } else if (inContext(DynamicViewDslContext.class)) { - new DynamicViewContentParser().parseRelationship(getContext(DynamicViewDslContext.class), tokens); + 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); diff --git a/src/test/dsl/test.dsl b/src/test/dsl/test.dsl index 4dcd877..cab02b5 100644 --- a/src/test/dsl/test.dsl +++ b/src/test/dsl/test.dsl @@ -228,6 +228,10 @@ workspace "Name" "Description" { description "Description" user -> homePageController "Requests via web browser" + homePageController -> user { + url "https://structurizr.com" + } + autoLayout properties { diff --git a/src/test/java/com/structurizr/dsl/DynamicViewRelationshipParserTests.java b/src/test/java/com/structurizr/dsl/DynamicViewRelationshipParserTests.java new file mode 100644 index 0000000..2bd6cf0 --- /dev/null +++ b/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 ", 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 ", 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