Skip to content
This repository has been archived by the owner on Jan 10, 2024. It is now read-only.

Commit

Permalink
Fixes some clashing issues with automatic key generation for views.
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbrowndotje committed Feb 3, 2023
1 parent d24d70f commit 17026e7
Show file tree
Hide file tree
Showing 23 changed files with 121 additions and 37 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ targetCompatibility = 1.8

description = 'Structurizr DSL'
group = 'com.structurizr'
version = '1.24.0'
version = '1.25.0'

test {
useJUnitPlatform()
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.25.0 (3rd February 2023)

- Fixes some clashing issues with automatic key generation for views.

## 1.24.0 (28th January 2023)

- More variables are exposed to scripts, based upon where the script is defined (`element`, `relationship`, `view`).
Expand Down
16 changes: 13 additions & 3 deletions docs/language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Please see the [DSL cookbook](cookbook) for a tutorial guide to the Structurizr

In addition, workspaces are subject to the following rules:

- Each view must have a unique "key" (this is autogenerated if not specified).
- Each view must have a unique "key" (this is generated for you if not specified; __warning: you will likely lose manual layout information when using automatically generated view keys__).
- Software and people names must be unique.
- Container names must be unique within the context of a software system.
- Component names must be unique within the context of a container.
Expand Down Expand Up @@ -927,8 +927,6 @@ Or, if you're extending a JSON-based workspace, you can reference an element by

See [ref.dsl](../src/test/dsl/ref.dsl) for some usage examples.

__Please note that `!ref` is currently an experimental feature.__

### views

Each workspace can also contain one or more views, defined with the `views` block.
Expand Down Expand Up @@ -966,6 +964,8 @@ systemLandscape [key] [description] {
}
```

A view key will be generated for you if not specified; __you will likely lose manual layout information when using automatically generated view keys__.

Permitted children:

- [include](#include)
Expand All @@ -986,6 +986,8 @@ systemContext <software system identifier> [key] [description] {
}
```

A view key will be generated for you if not specified; __you will likely lose manual layout information when using automatically generated view keys__.

Permitted children:

- [include](#include)
Expand All @@ -1006,6 +1008,8 @@ container <software system identifier> [key] [description] {
}
```

A view key will be generated for you if not specified; __you will likely lose manual layout information when using automatically generated view keys__.

Permitted children:

- [include](#include)
Expand All @@ -1026,6 +1030,8 @@ component <container identifier> [key] [description] {
}
```

A view key will be generated for you if not specified; __you will likely lose manual layout information when using automatically generated view keys__.

Permitted children:

- [include](#include)
Expand Down Expand Up @@ -1068,6 +1074,8 @@ The first property defines the scope of the view, and therefore what can be adde
- Software system scope: People, other software systems, and containers.
- Container scope: People, other software systems, other containers, and components.

A view key will be generated for you if not specified; __you will likely lose manual layout information when using automatically generated view keys__.

Unlike the other diagram types, Dynamic views are created by specifying the relationships that should be added to the view, within the `dynamic` block, as follows:

```
Expand Down Expand Up @@ -1101,6 +1109,8 @@ The first property defines the scope of the view, and the second property define
- `*` scope: All deployment nodes, infrastructure nodes, and container instances within the deployment environment.
- Software system scope: All deployment nodes and infrastructure nodes within the deployment environment. Container instances within the deployment environment that belong to the software system.

A view key will be generated for you if not specified; __you will likely lose manual layout information when using automatically generated view keys__.

Permitted children:

- [include](#include)
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/com/structurizr/dsl/AbstractViewParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.structurizr.dsl;

import com.structurizr.Workspace;
import com.structurizr.view.FilteredView;
import com.structurizr.view.SystemLandscapeView;
import com.structurizr.view.View;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

abstract class AbstractViewParser extends AbstractParser {

protected String generateViewKey(Workspace workspace, String prefix) {
int counter = 1;
String key = prefix + "-" + counter;

while (hasViewWithKey(workspace, key)) {
counter++;
key = prefix + "-" + counter;
}

return key;
}

protected boolean hasViewWithKey(Workspace workspace, String key) {
Set<View> views = new HashSet<>();
views.addAll(workspace.getViews().getCustomViews());
views.addAll(workspace.getViews().getSystemLandscapeViews());
views.addAll(workspace.getViews().getSystemContextViews());
views.addAll(workspace.getViews().getContainerViews());
views.addAll(workspace.getViews().getComponentViews());
views.addAll(workspace.getViews().getDynamicViews());
views.addAll(workspace.getViews().getDeploymentViews());

Collection<FilteredView> filteredViews = workspace.getViews().getFilteredViews();

return
views.stream().anyMatch(view -> view.getKey().equals(key)) ||
filteredViews.stream().anyMatch(view -> view.getKey().equals(key));
}

}
4 changes: 2 additions & 2 deletions src/main/java/com/structurizr/dsl/ComponentViewParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.structurizr.model.Element;
import com.structurizr.view.ComponentView;

final class ComponentViewParser extends AbstractParser {
final class ComponentViewParser extends AbstractViewParser {

private static final String GRAMMAR = "component <container identifier> [key] [description] {";

Expand Down Expand Up @@ -45,7 +45,7 @@ ComponentView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = removeNonWordCharacters(container.getSoftwareSystem().getName()) + "-" + removeNonWordCharacters(container.getName()) + "-" + VIEW_TYPE;
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/structurizr/dsl/ContainerViewParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.structurizr.model.SoftwareSystem;
import com.structurizr.view.ContainerView;

final class ContainerViewParser extends AbstractParser {
final class ContainerViewParser extends AbstractViewParser {

private static final String GRAMMAR = "container <software system identifier> [key] [description] {";

Expand Down Expand Up @@ -45,7 +45,7 @@ ContainerView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = key = removeNonWordCharacters(softwareSystem.getName()) + "-" + VIEW_TYPE;
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/structurizr/dsl/CustomViewParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.structurizr.Workspace;
import com.structurizr.view.CustomView;

final class CustomViewParser extends AbstractParser {
final class CustomViewParser extends AbstractViewParser {

private static final String GRAMMAR = "custom [key] [title] [description] {";

Expand All @@ -28,7 +28,7 @@ CustomView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = VIEW_TYPE + (context.getWorkspace().getViews().getCustomViews().size() + 1);
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/structurizr/dsl/DeploymentViewParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.structurizr.model.SoftwareSystem;
import com.structurizr.view.DeploymentView;

final class DeploymentViewParser extends AbstractParser {
final class DeploymentViewParser extends AbstractViewParser {

private static final String GRAMMAR = "deployment <*|software system identifier> <environment> [key] [description] {";

Expand Down Expand Up @@ -53,7 +53,7 @@ DeploymentView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = removeNonWordCharacters(environment) + "-" + VIEW_TYPE;
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand All @@ -68,7 +68,7 @@ DeploymentView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = removeNonWordCharacters(element.getName()) + "-" + removeNonWordCharacters(environment) + "-" + VIEW_TYPE;
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand Down
8 changes: 4 additions & 4 deletions src/main/java/com/structurizr/dsl/DynamicViewParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import java.text.DecimalFormat;

class DynamicViewParser extends AbstractParser {
class DynamicViewParser extends AbstractViewParser {

private static final String GRAMMAR = "dynamic <*|software system identifier|container identifier> [key] [description] {";

Expand Down Expand Up @@ -47,7 +47,7 @@ DynamicView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = VIEW_TYPE + "-" + format.format(workspace.getViews().getDynamicViews().size() + 1);
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand All @@ -62,7 +62,7 @@ DynamicView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = removeNonWordCharacters(element.getName()) + "-" + VIEW_TYPE + "-" + format.format(workspace.getViews().getDynamicViews().size() + 1);
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand All @@ -72,7 +72,7 @@ DynamicView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = removeNonWordCharacters(container.getSoftwareSystem().getName()) + "-" + removeNonWordCharacters(container.getName()) + "-" + VIEW_TYPE + "-" + format.format(workspace.getViews().getDynamicViews().size() + 1);
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand Down
5 changes: 2 additions & 3 deletions src/main/java/com/structurizr/dsl/FilteredViewParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import java.util.HashSet;
import java.util.Set;

final class FilteredViewParser extends AbstractParser {
final class FilteredViewParser extends AbstractViewParser {

private static final String GRAMMAR = "filtered <baseKey> <include|exclude> <tags> [key] [description]";

Expand Down Expand Up @@ -79,11 +79,10 @@ FilteredView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = baseView.getKey() + "-" + VIEW_TYPE + "-" + format.format(workspace.getViews().getDynamicViews().size() + 1);
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);


return workspace.getViews().createFilteredView(baseView, key, description, filterMode, tags.toArray(new String[0]));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.structurizr.model.SoftwareSystem;
import com.structurizr.view.SystemContextView;

final class SystemContextViewParser extends AbstractParser {
final class SystemContextViewParser extends AbstractViewParser {

private static final String GRAMMAR = "systemContext <software system identifier> [key] [description] {";

Expand Down Expand Up @@ -45,7 +45,7 @@ SystemContextView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = removeNonWordCharacters(softwareSystem.getName()) + "-" + VIEW_TYPE;
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.structurizr.Workspace;
import com.structurizr.view.SystemLandscapeView;

final class SystemLandscapeViewParser extends AbstractParser {
final class SystemLandscapeViewParser extends AbstractViewParser {

private static final String GRAMMAR = "systemLandscape [key] [description] {";

Expand All @@ -26,7 +26,7 @@ SystemLandscapeView parse(DslContext context, Tokens tokens) {
if (tokens.includes(KEY_INDEX)) {
key = tokens.get(KEY_INDEX);
} else {
key = VIEW_TYPE;
key = generateViewKey(workspace, VIEW_TYPE);
}
validateViewKey(key);

Expand Down
18 changes: 18 additions & 0 deletions src/test/dsl/views-without-keys.dsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
workspace {

model {
person "User"
}

views {
systemLandscape {
}

systemLandscape {
}

systemLandscape {
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ void test_parse_CreatesAComponentView() {
List<ComponentView> views = new ArrayList<>(context.getWorkspace().getViews().getComponentViews());

assertEquals(1, views.size());
assertEquals("Name-Container-Component", views.get(0).getKey());
assertEquals("Component-1", views.get(0).getKey());
assertEquals("", views.get(0).getDescription());
assertTrue(views.get(0).getExternalContainerBoundariesVisible());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ void test_parse_CreatesAContainerView() {
List<ContainerView> views = new ArrayList<>(context.getWorkspace().getViews().getContainerViews());

assertEquals(1, views.size());
assertEquals("Name-Container", views.get(0).getKey());
assertEquals("Container-1", views.get(0).getKey());
assertEquals("", views.get(0).getDescription());
assertTrue(views.get(0).getExternalSoftwareSystemBoundariesVisible());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void test_parse_CreatesACustomView() {
List<CustomView> views = new ArrayList<>(context.getWorkspace().getViews().getCustomViews());

assertEquals(1, views.size());
assertEquals("Custom1", views.get(0).getKey());
assertEquals("Custom-1", views.get(0).getKey());
assertEquals("", views.get(0).getTitle());
assertEquals("", views.get(0).getDescription());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void test_parse_CreatesADeploymentViewWithNoScope() {
List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews());

assertEquals(1, views.size());
assertEquals("Live-Deployment", views.get(0).getKey());
assertEquals("Deployment-1", views.get(0).getKey());
assertEquals("", views.get(0).getDescription());
assertNull(views.get(0).getSoftwareSystem());
}
Expand Down Expand Up @@ -163,7 +163,7 @@ void test_parse_CreatesADeploymentViewWithSoftwareSystemScope() {
List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews());

assertEquals(1, views.size());
assertEquals("Name-Live-Deployment", views.get(0).getKey());
assertEquals("Deployment-1", views.get(0).getKey());
assertEquals("", views.get(0).getDescription());
assertSame(softwareSystem, views.get(0).getSoftwareSystem());
}
Expand Down
Loading

0 comments on commit 17026e7

Please sign in to comment.