Skip to content

Commit

Permalink
Deprecates !ref and !extend in favour of !element and `!relatio…
Browse files Browse the repository at this point in the history
…nship`.
  • Loading branch information
simonbrowndotje committed Sep 18, 2024
1 parent bb68483 commit f26f63d
Show file tree
Hide file tree
Showing 19 changed files with 271 additions and 53 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
- 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.
- structurizr-dsl: Adds support for element technology expressions (e.g. `element.technology==Java` and `element.technology!=Java`).
- structurizr-dsl: Deprecates `!ref` and `!extend`.
- structurizr-dsl: Adds an `!element` keyword that can be used to find a single element by identifier or canonical name (replaces `!ref` and `!extend`).
- structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression.
- structurizr-dsl: Adds an `!relationship` keyword that can be used to find a single relationship by identifier (replaces `!ref` and `!extend`).
- structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression.
- structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder.
- structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.structurizr.dsl;

import com.structurizr.model.Element;
import com.structurizr.model.StaticStructureElement;

final class FindElementParser extends AbstractParser {

private static final String GRAMMAR = "!element <identifier|canonical name>";

private final static int IDENTIFIER_INDEX = 1;

Element parse(DslContext context, Tokens tokens) {
// !element <identifier|canonical name>

if (tokens.hasMoreThan(IDENTIFIER_INDEX)) {
throw new RuntimeException("Too many tokens, expected: " + GRAMMAR);
}

if (!tokens.includes(IDENTIFIER_INDEX)) {
throw new RuntimeException("Expected: " + GRAMMAR);
}

String s = tokens.get(IDENTIFIER_INDEX);

Element element;

if (s.contains("://")) {
element = context.getWorkspace().getModel().getElementWithCanonicalName(s);
} else {
element = context.getElement(s);
}

if (element == null) {
throw new RuntimeException("An element identified by \"" + s + "\" could not be found");
}

if (context instanceof GroupableDslContext && element instanceof StaticStructureElement) {
GroupableDslContext groupableDslContext = (GroupableDslContext)context;
StaticStructureElement staticStructureElement = (StaticStructureElement)element;
if (groupableDslContext.hasGroup()) {
staticStructureElement.setGroup(groupableDslContext.getGroup().getName());
}
}

return element;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.util.Set;
import java.util.stream.Collectors;

final class ElementsParser extends AbstractParser {
final class FindElementsParser extends AbstractParser {

private static final String GRAMMAR = "!elements <expression>";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.structurizr.dsl;

import com.structurizr.model.Relationship;

final class FindRelationshipParser extends AbstractParser {

private static final String GRAMMAR = "!relationship <identifier>";

private final static int IDENTIFIER_INDEX = 1;

Relationship parse(DslContext context, Tokens tokens) {
// !relationship <identifier|canonical name>

if (tokens.hasMoreThan(IDENTIFIER_INDEX)) {
throw new RuntimeException("Too many tokens, expected: " + GRAMMAR);
}

if (!tokens.includes(IDENTIFIER_INDEX)) {
throw new RuntimeException("Expected: " + GRAMMAR);
}

String s = tokens.get(IDENTIFIER_INDEX);

Relationship relationship = context.getRelationship(s);

if (relationship == null) {
throw new RuntimeException("A relationship identified by \"" + s + "\" could not be found");
}

return relationship;
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.structurizr.dsl;

import com.structurizr.model.Element;
import com.structurizr.model.ModelItem;
import com.structurizr.model.Relationship;

import java.util.Set;
import java.util.stream.Collectors;

final class RelationshipsParser extends AbstractParser {
final class FindRelationshipsParser extends AbstractParser {

private static final String GRAMMAR = "!relationships <expression>";

Expand All @@ -25,7 +24,6 @@ Set<Relationship> parse(DslContext context, Tokens tokens) {

Set<Relationship> relationships = modelItems.stream().filter(mi -> mi instanceof Relationship).map(mi -> (Relationship)mi).collect(Collectors.toSet());


if (relationships.isEmpty()) {
throw new RuntimeException("No relationships found for expression \"" + expression + "\"");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,20 @@ void parse(List<String> lines, File dslFile, boolean fragment, boolean includeIn
startContext(new RelationshipsDslContext(getContext(), relationships));
}

} else if ((REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelItemDslContext.class) || inContext(ModelDslContext.class))) {
ModelItem modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken());
} else if ((FIND_ELEMENT_TOKEN.equalsIgnoreCase(firstToken) || FIND_RELATIONSHIP_TOKEN.equalsIgnoreCase(firstToken) || REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelItemDslContext.class) || inContext(ModelDslContext.class))) {
ModelItem modelItem = null;

if (REF_TOKEN.equalsIgnoreCase(firstToken)) {
log.warn(REF_TOKEN + " has been deprecated and will be removed in a future release - please use !element or !relationship instead");
modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken());
} else if (EXTEND_TOKEN.equalsIgnoreCase(firstToken)) {
log.warn(EXTEND_TOKEN + " has been deprecated and will be removed in a future release - please use !element or !relationship instead");
modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken());
} else if (FIND_ELEMENT_TOKEN.equalsIgnoreCase(firstToken)) {
modelItem = new FindElementParser().parse(getContext(), tokens.withoutContextStartToken());
} else if (FIND_RELATIONSHIP_TOKEN.equalsIgnoreCase(firstToken)) {
modelItem = new FindRelationshipParser().parse(getContext(), tokens.withoutContextStartToken());
}

if (shouldStartContext(tokens)) {
if (modelItem instanceof Person) {
Expand Down Expand Up @@ -361,15 +373,15 @@ void parse(List<String> lines, File dslFile, boolean fragment, boolean includeIn
}
}

} else if (ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) {
Set<Element> elements = new ElementsParser().parse(getContext(), tokens.withoutContextStartToken());
} else if (FIND_ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) {
Set<Element> elements = new FindElementsParser().parse(getContext(), tokens.withoutContextStartToken());

if (shouldStartContext(tokens)) {
startContext(new ElementsDslContext(getContext(), elements));
}

} else if (RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) {
Set<Relationship> relationships = new RelationshipsParser().parse(getContext(), tokens.withoutContextStartToken());
} else if (FIND_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) {
Set<Relationship> relationships = new FindRelationshipsParser().parse(getContext(), tokens.withoutContextStartToken());

if (shouldStartContext(tokens)) {
startContext(new RelationshipsDslContext(getContext(), relationships));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,15 @@ class StructurizrDslTokens {
static final String VAR_TOKEN = "!var";
static final String IDENTIFIERS_TOKEN = "!identifiers";
static final String IMPLIED_RELATIONSHIPS_TOKEN = "!impliedRelationships";
static final String REF_TOKEN = "!ref";
static final String ELEMENTS_TOKEN = "!elements";
static final String RELATIONSHIPS_TOKEN = "!relationships";

static final String EXTEND_TOKEN = "!extend";
static final String REF_TOKEN = "!ref"; // deprecated
static final String EXTEND_TOKEN = "!extend"; // deprecated

static final String FIND_ELEMENT_TOKEN = "!element";
static final String FIND_ELEMENTS_TOKEN = "!elements";
static final String FIND_RELATIONSHIP_TOKEN = "!relationship";
static final String FIND_RELATIONSHIPS_TOKEN = "!relationships";

static final String PLUGIN_TOKEN = "!plugin";
static final String SCRIPT_TOKEN = "!script";

Expand Down
31 changes: 15 additions & 16 deletions structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -516,15 +516,28 @@ void test_extendWorkspaceFromDslFiles() throws Exception {
}

@Test
void test_ref() throws Exception {
void test_findElement() throws Exception {
StructurizrDslParser parser = new StructurizrDslParser();
parser.parse(new File("src/test/resources/dsl/ref.dsl"));
parser.parse(new File("src/test/resources/dsl/find-element.dsl"));

assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/New deployment node/New infrastructure node"));
assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 1/New infrastructure node 1"));
assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 2/New infrastructure node 2"));
}

@Test
void test_findElement_Hierachical() throws Exception {
File dslFile = new File("src/test/resources/dsl/find-element-hierarchical.dsl");

StructurizrDslParser parser = new StructurizrDslParser();
parser.parse(dslFile);

Component component = parser.getWorkspace().getModel().getSoftwareSystemWithName("A").getContainerWithName("B").getComponentWithName("C");
assertEquals("Value1", component.getProperties().get("Name1"));
assertEquals("Value2", component.getProperties().get("Name2"));
assertEquals("Value3", component.getProperties().get("Name3"));
}

@Test
void test_parallel1() throws Exception {
StructurizrDslParser parser = new StructurizrDslParser();
Expand Down Expand Up @@ -1285,18 +1298,4 @@ void test_ImageView_WhenParserIsInRestrictedMode() {
}
}

@Test
void test_extendHierachical() throws Exception {
File dslFile = new File("src/test/resources/dsl/extend-hierarchical.dsl");

StructurizrDslParser parser = new StructurizrDslParser();
parser.parse(dslFile);

Component component = parser.getWorkspace().getModel().getSoftwareSystemWithName("A").getContainerWithName("B").getComponentWithName("C");
assertEquals("Value1", component.getProperties().get("Name1"));
assertEquals("Value2", component.getProperties().get("Name2"));
assertEquals("Value3", component.getProperties().get("Name3"));
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.structurizr.dsl;

import com.structurizr.model.ModelItem;
import com.structurizr.model.Person;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class FindElementParserTests extends AbstractTests {

private final FindElementParser parser = new FindElementParser();

@Test
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {
try {
parser.parse(context(), tokens("!element", "name", "tokens"));
fail();
} catch (Exception e) {
assertEquals("Too many tokens, expected: !element <identifier|canonical name>", e.getMessage());
}
}

@Test
void test_parse_ThrowsAnException_WhenTheIdentifierOrCanonicalNameIsNotSpecified() {
try {
parser.parse(context(), tokens("!element"));
fail();
} catch (Exception e) {
assertEquals("Expected: !element <identifier|canonical name>", e.getMessage());
}
}

@Test
void test_parse_ThrowsAnException_WhenTheReferencedElementCannotBeFound() {
try {
parser.parse(context(), tokens("!element", "Person://User"));
fail();
} catch (Exception e) {
assertEquals("An element identified by \"Person://User\" could not be found", e.getMessage());
}
}

@Test
void test_parse_FindsAnElementByCanonicalName() {
Person user = workspace.getModel().addPerson("User");
ModelItem element = parser.parse(context(), tokens("!element", "Person://User"));

assertSame(user, element);
}

@Test
void test_parse_FindsAnElementByIdentifier() {
Person user = workspace.getModel().addPerson("User");

ModelDslContext context = context();
IdentifiersRegister register = new IdentifiersRegister();
register.register("user", user);
context.setIdentifierRegister(register);

ModelItem modelItem = parser.parse(context, tokens("!element", "user"));
assertSame(modelItem, user);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
import com.structurizr.model.Component;
import com.structurizr.model.Container;
import com.structurizr.model.Element;
import com.structurizr.model.SoftwareSystem;
import org.junit.jupiter.api.Test;

import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;

class ElementsParserTests extends AbstractTests {
class FindElementsParserTests extends AbstractTests {

private final ElementsParser parser = new ElementsParser();
private final FindElementsParser parser = new FindElementsParser();

@Test
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.structurizr.dsl;

import com.structurizr.model.ModelItem;
import com.structurizr.model.Person;
import com.structurizr.model.Relationship;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class FindRelationshipParserTests extends AbstractTests {

private final FindRelationshipParser parser = new FindRelationshipParser();

@Test
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {
try {
parser.parse(context(), tokens("!relationship", "name", "tokens"));
fail();
} catch (Exception e) {
assertEquals("Too many tokens, expected: !relationship <identifier>", e.getMessage());
}
}

@Test
void test_parse_ThrowsAnException_WhenTheIdentifierIsNotSpecified() {
try {
parser.parse(context(), tokens("!relationship"));
fail();
} catch (Exception e) {
assertEquals("Expected: !relationship <identifier>", e.getMessage());
}
}

@Test
void test_parse_ThrowsAnException_WhenTheReferencedRelationshipCannotBeFound() {
try {
parser.parse(context(), tokens("!relationship", "rel"));
fail();
} catch (Exception e) {
assertEquals("A relationship identified by \"rel\" could not be found", e.getMessage());
}
}

@Test
void test_parse_FindsARelationshipByIdentifier() {
Person user = workspace.getModel().addPerson("User");
Relationship relationship = user.interactsWith(user, "Description");

ModelDslContext context = context();
IdentifiersRegister register = new IdentifiersRegister();
register.register("rel", relationship);
context.setIdentifierRegister(register);

ModelItem modelItem = parser.parse(context, tokens("!relationship", "rel"));
assertSame(modelItem, relationship);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

import static org.junit.jupiter.api.Assertions.*;

class RelationshipsParserTests extends AbstractTests {
class FindRelationshipsParserTests extends AbstractTests {

private final RelationshipsParser parser = new RelationshipsParser();
private final FindRelationshipsParser parser = new FindRelationshipsParser();

@Test
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {
Expand Down
Loading

0 comments on commit f26f63d

Please sign in to comment.