diff --git a/build.gradle b/build.gradle index a2bce9a..1b27139 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ targetCompatibility = 11 description = 'Structurizr DSL' group = 'com.structurizr' -version = '1.32.0' +version = '1.32.1' test { useJUnitPlatform() diff --git a/docs/changelog.md b/docs/changelog.md index c342fcc..c50f3c0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.32.1 (unreleased) + +- Fixes https://github.com/structurizr/dsl/issues/324 (Groups with no curly braces breaks diagrams). + ## 1.32.0 (28th July 2023) - Adds the ability to specify the workspace `visibility` (private/public) via the workspace configuration. diff --git a/src/main/java/com/structurizr/dsl/GroupParser.java b/src/main/java/com/structurizr/dsl/GroupParser.java index 684b166..8ff6e0f 100644 --- a/src/main/java/com/structurizr/dsl/GroupParser.java +++ b/src/main/java/com/structurizr/dsl/GroupParser.java @@ -5,15 +5,20 @@ 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 + // group { - if (tokens.hasMoreThan(NAME_INDEX)) { + if (tokens.hasMoreThan(BRACE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } - if (!tokens.includes(NAME_INDEX)) { + 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); } diff --git a/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index dc44711..186df51 100644 --- a/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -355,37 +355,37 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio registerIdentifier(identifier, component); } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { - ElementGroup group = new GroupParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); + 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.withoutContextStartToken()); + 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.withoutContextStartToken()); + 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.withoutContextStartToken()); + 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.withoutContextStartToken()); + 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.withoutContextStartToken()); + ElementGroup group = new GroupParser().parse(getContext(DeploymentNodeDslContext.class), tokens); DeploymentNode deploymentNode = getContext(DeploymentNodeDslContext.class).getDeploymentNode(); startContext(new DeploymentNodeDslContext(deploymentNode, group)); diff --git a/src/test/dsl/group-without-brace.dsl b/src/test/dsl/group-without-brace.dsl new file mode 100644 index 0000000..71647fe --- /dev/null +++ b/src/test/dsl/group-without-brace.dsl @@ -0,0 +1,7 @@ +workspace { + + model { + group "Name" + } + +} \ No newline at end of file diff --git a/src/test/java/com/structurizr/dsl/DslTests.java b/src/test/java/com/structurizr/dsl/DslTests.java index 425478b..dec8a70 100644 --- a/src/test/java/com/structurizr/dsl/DslTests.java +++ b/src/test/java/com/structurizr/dsl/DslTests.java @@ -1161,4 +1161,17 @@ void test_IncludeImpliedRelationship() throws Exception { assertEquals(1, parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getRelationships().size()); } + @Test + void test_GroupWithoutBrace() throws Exception { + File dslFile = new File("src/test/dsl/group-without-brace.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Expected: group { at line 4 of " + dslFile.getAbsolutePath() + ": group \"Name\"", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/src/test/java/com/structurizr/dsl/GroupParserTests.java b/src/test/java/com/structurizr/dsl/GroupParserTests.java index 6f6f469..0186144 100644 --- a/src/test/java/com/structurizr/dsl/GroupParserTests.java +++ b/src/test/java/com/structurizr/dsl/GroupParserTests.java @@ -11,7 +11,7 @@ class GroupParserTests extends AbstractTests { @Test void parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(null, tokens("group", "name", "extra")); + parser.parse(null, tokens("group", "name", "{", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: group {", e.getMessage()); @@ -28,9 +28,19 @@ void parse_ThrowsAnException_WhenTheNameIsMissing() { } } + @Test + void parse_ThrowsAnException_WhenTheBraceIsMissing() { + try { + parser.parse(null, tokens("group", "Name", "foo")); + fail(); + } catch (Exception e) { + assertEquals("Expected: group {", e.getMessage()); + } + } + @Test void parse() { - ElementGroup group = parser.parse(context(), tokens("group", "Group 1")); + ElementGroup group = parser.parse(context(), tokens("group", "Group 1", "{")); assertEquals("Group 1", group.getName()); assertTrue(group.getElements().isEmpty()); } @@ -41,7 +51,7 @@ void parse_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { context.setWorkspace(workspace); try { - parser.parse(context, tokens("group", "Group 2")); + 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()); @@ -54,7 +64,7 @@ void parse_NestedGroup() { ModelDslContext context = new ModelDslContext(new ElementGroup(workspace.getModel(), "Group 1")); context.setWorkspace(workspace); - ElementGroup group = parser.parse(context, tokens("group", "Group 2")); + ElementGroup group = parser.parse(context, tokens("group", "Group 2", "{")); assertEquals("Group 1/Group 2", group.getName()); assertTrue(group.getElements().isEmpty()); }