Skip to content

Commit 2546f30

Browse files
Adds an 'instanceOf' keyword (an alternative for softwareSystemInstance and containerInstance).
1 parent 7eacd41 commit 2546f30

File tree

9 files changed

+199
-2
lines changed

9 files changed

+199
-2
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- structurizr-dsl: Adds a `Bucket` shape.
88
- structurizr-dsl: Adds a `Shell` shape.
99
- structurizr-dsl: Adds a `Terminal` shape.
10+
- structurizr-dsl: Adds an 'instanceOf' keyword (an alternative for `softwareSystemInstance` and `containerInstance`).
1011

1112
## v4.1.0 (28th May 2025)
1213

structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ protected String[] getPermittedTokens() {
3838
StructurizrDslTokens.GROUP_TOKEN,
3939
StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN,
4040
StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN,
41+
StructurizrDslTokens.INSTANCE_OF_TOKEN,
4142
StructurizrDslTokens.SOFTWARE_SYSTEM_INSTANCE_TOKEN,
4243
StructurizrDslTokens.CONTAINER_INSTANCE_TOKEN,
4344
StructurizrDslTokens.RELATIONSHIP_TOKEN,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.structurizr.dsl;
2+
3+
import com.structurizr.model.*;
4+
5+
final class InstanceOfParser {
6+
7+
private static final String GRAMMAR = "instanceOf <identifier> [deploymentGroups] [tags]";
8+
9+
private static final int IDENTIFIER_INDEX = 1;
10+
private static final int TAGS_INDEX = 3;
11+
12+
StaticStructureElementInstance parse(DeploymentNodeDslContext context, Tokens tokens) {
13+
// instanceOf <identifier> [tags]
14+
// instanceOf <identifier> [deploymentGroup] [tags]
15+
16+
if (tokens.hasMoreThan(TAGS_INDEX)) {
17+
throw new RuntimeException("Too many tokens, expected: " + GRAMMAR);
18+
}
19+
20+
if (!tokens.includes(IDENTIFIER_INDEX)) {
21+
throw new RuntimeException("Expected: " + GRAMMAR);
22+
}
23+
24+
String elementIdentifier = tokens.get(IDENTIFIER_INDEX);
25+
26+
Element element = context.getElement(elementIdentifier);
27+
if (element == null) {
28+
throw new RuntimeException("The element \"" + elementIdentifier + "\" does not exist");
29+
}
30+
31+
if (element instanceof SoftwareSystem) {
32+
return new SoftwareSystemInstanceParser().parse(context, tokens);
33+
} else if (element instanceof Container) {
34+
return new ContainerInstanceParser().parse(context, tokens);
35+
} else {
36+
throw new RuntimeException("The element \"" + elementIdentifier + "\" must be a software system or a container");
37+
}
38+
}
39+
40+
}

structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,21 @@ void parse(List<String> lines, File dslFile, boolean fragment, boolean includeIn
916916

917917
registerIdentifier(identifier, infrastructureNode);
918918

919+
} else if (INSTANCE_OF_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) {
920+
StaticStructureElementInstance instance = new InstanceOfParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken());
921+
922+
if (instance instanceof SoftwareSystemInstance) {
923+
if (shouldStartContext(tokens)) {
924+
startContext(new SoftwareSystemInstanceDslContext((SoftwareSystemInstance)instance));
925+
}
926+
} else if (instance instanceof ContainerInstance) {
927+
if (shouldStartContext(tokens)) {
928+
startContext(new ContainerInstanceDslContext((ContainerInstance)instance));
929+
}
930+
}
931+
932+
registerIdentifier(identifier, instance);
933+
919934
} else if (SOFTWARE_SYSTEM_INSTANCE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) {
920935
SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstanceParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken());
921936

structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class StructurizrDslTokens {
3535
static final String DEPLOYMENT_GROUP_TOKEN = "deploymentGroup";
3636
static final String DEPLOYMENT_NODE_TOKEN = "deploymentNode";
3737
static final String INFRASTRUCTURE_NODE_TOKEN = "infrastructureNode";
38+
static final String INSTANCE_OF_TOKEN = "instanceOf";
3839
static final String SOFTWARE_SYSTEM_INSTANCE_TOKEN = "softwareSystemInstance";
3940
static final String CONTAINER_INSTANCE_TOKEN = "containerInstance";
4041
static final String HEALTH_CHECK_TOKEN = "healthCheck";

structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class ContainerInstanceParserTests extends AbstractTests {
99

10-
private ContainerInstanceParser parser = new ContainerInstanceParser();
10+
private final ContainerInstanceParser parser = new ContainerInstanceParser();
1111

1212
@Test
1313
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.structurizr.dsl;
2+
3+
import com.structurizr.model.*;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.*;
7+
8+
class InstanceOfParserTests extends AbstractTests {
9+
10+
private final InstanceOfParser parser = new InstanceOfParser();
11+
12+
@Test
13+
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {
14+
try {
15+
parser.parse(new DeploymentNodeDslContext(null), tokens("instanceOf", "identifier", "deploymentGroups", "tags", "extra"));
16+
fail();
17+
} catch (Exception e) {
18+
assertEquals("Too many tokens, expected: instanceOf <identifier> [deploymentGroups] [tags]", e.getMessage());
19+
}
20+
}
21+
22+
@Test
23+
void test_parse_ThrowsAnException_WhenTheIdentifierIsNotSpecified() {
24+
try {
25+
parser.parse(new DeploymentNodeDslContext(null), tokens("instanceOf"));
26+
fail();
27+
} catch (Exception e) {
28+
assertEquals("Expected: instanceOf <identifier> [deploymentGroups] [tags]", e.getMessage());
29+
}
30+
}
31+
32+
@Test
33+
void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() {
34+
try {
35+
parser.parse(new DeploymentNodeDslContext(null), tokens("instanceOf", "softwareSystem"));
36+
fail();
37+
} catch (Exception e) {
38+
assertEquals("The element \"softwareSystem\" does not exist", e.getMessage());
39+
}
40+
}
41+
42+
@Test
43+
void test_parse_ThrowsAnException_WhenTheElementIsNotASoftwareSystem() {
44+
DeploymentNodeDslContext context = new DeploymentNodeDslContext(null);
45+
IdentifiersRegister elements = new IdentifiersRegister();
46+
elements.register("softwaresystem", model.addPerson("Name", "Description"));
47+
context.setIdentifierRegister(elements);
48+
49+
try {
50+
parser.parse(context, tokens("instanceOf", "softwareSystem"));
51+
fail();
52+
} catch (Exception e) {
53+
assertEquals("The element \"softwareSystem\" must be a software system or a container", e.getMessage());
54+
}
55+
}
56+
57+
@Test
58+
void test_parse_CreatesASoftwareSystemInstance() {
59+
SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description");
60+
DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology");
61+
DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode);
62+
IdentifiersRegister elements = new IdentifiersRegister();
63+
elements.register("softwaresystem", softwareSystem);
64+
context.setIdentifierRegister(elements);
65+
66+
parser.parse(context, tokens("instanceOf", "softwareSystem"));
67+
68+
assertEquals(3, model.getElements().size());
69+
assertEquals(1, deploymentNode.getSoftwareSystemInstances().size());
70+
SoftwareSystemInstance softwareSystemInstance = deploymentNode.getSoftwareSystemInstances().iterator().next();
71+
assertSame(softwareSystem, softwareSystemInstance.getSoftwareSystem());
72+
assertEquals("Software System Instance", softwareSystemInstance.getTags());
73+
assertEquals("Live", softwareSystemInstance.getEnvironment());
74+
assertEquals(1, softwareSystemInstance.getDeploymentGroups().size());
75+
assertEquals("Default", softwareSystemInstance.getDeploymentGroups().iterator().next());
76+
}
77+
78+
@Test
79+
void test_parse_ThrowsAnException_WhenTheElementIsNotAContainer() {
80+
DeploymentNodeDslContext context = new DeploymentNodeDslContext(null);
81+
IdentifiersRegister elements = new IdentifiersRegister();
82+
elements.register("container", model.addPerson("Name", "Description"));
83+
context.setIdentifierRegister(elements);
84+
85+
try {
86+
parser.parse(context, tokens("instanceOf", "container"));
87+
fail();
88+
} catch (Exception e) {
89+
assertEquals("The element \"container\" must be a software system or a container", e.getMessage());
90+
}
91+
}
92+
93+
@Test
94+
void test_parse_CreatesAContainerInstance() {
95+
SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description");
96+
Container container = softwareSystem.addContainer("Container", "Description", "Technology");
97+
DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology");
98+
DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode);
99+
IdentifiersRegister elements = new IdentifiersRegister();
100+
elements.register("container", container);
101+
context.setIdentifierRegister(elements);
102+
103+
parser.parse(context, tokens("instanceOf", "container"));
104+
105+
assertEquals(4, model.getElements().size());
106+
assertEquals(1, deploymentNode.getContainerInstances().size());
107+
ContainerInstance containerInstance = deploymentNode.getContainerInstances().iterator().next();
108+
assertSame(container, containerInstance.getContainer());
109+
assertEquals("Container Instance", containerInstance.getTags());
110+
assertEquals("Live", containerInstance.getEnvironment());
111+
assertEquals(1, containerInstance.getDeploymentGroups().size());
112+
assertEquals("Default", containerInstance.getDeploymentGroups().iterator().next());
113+
}
114+
115+
}

structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class SoftwareSystemInstanceParserTests extends AbstractTests {
99

10-
private SoftwareSystemInstanceParser parser = new SoftwareSystemInstanceParser();
10+
private final SoftwareSystemInstanceParser parser = new SoftwareSystemInstanceParser();
1111

1212
@Test
1313
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {

structurizr-dsl/src/test/resources/dsl/test.dsl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,18 @@ workspace "Name" "Description" {
109109
healthCheck "Check 2" "https://example.com/health" 60
110110
healthCheck "Check 2" "https://example.com/health" 120 1000
111111
}
112+
instanceOf softwareSystem {
113+
url "https://structurizr.com"
114+
properties {
115+
"Name" "Value"
116+
}
117+
perspectives {
118+
"Security" "A description..."
119+
}
120+
healthCheck "Check 1" "https://example.com/health"
121+
healthCheck "Check 2" "https://example.com/health" 60
122+
healthCheck "Check 2" "https://example.com/health" 120 1000
123+
}
112124
}
113125
}
114126

@@ -138,6 +150,18 @@ workspace "Name" "Description" {
138150
healthCheck "Check 2" "https://example.com/health" 60
139151
healthCheck "Check 2" "https://example.com/health" 120 1000
140152
}
153+
instanceOf webApplication {
154+
url "https://structurizr.com"
155+
properties {
156+
"Name" "Value"
157+
}
158+
perspectives {
159+
"Security" "A description..."
160+
}
161+
healthCheck "Check 1" "https://example.com/health"
162+
healthCheck "Check 2" "https://example.com/health" 60
163+
healthCheck "Check 2" "https://example.com/health" 120 1000
164+
}
141165
}
142166

143167
url "https://structurizr.com"

0 commit comments

Comments
 (0)