Skip to content

Commit

Permalink
structurizr-dsl: Adds a way to specify the implied relationships stra…
Browse files Browse the repository at this point in the history
…tegy by a fully qualified class name via `!impliedRelationships`.
  • Loading branch information
simonbrowndotje committed Jun 30, 2024
1 parent 17964c5 commit 1366d3d
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 17 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 2.2.0 (unreleased)

- structurizr-dsl: Adds support for element/relationship property expressions (https://github.com/structurizr/java/issues/297).
- structurizr-dsl: Adds a way to specify the implied relationships strategy by a fully qualified class name via `!impliedRelationships`.

## 2.1.4 (18th June 2024)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,60 @@

import com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy;
import com.structurizr.model.DefaultImpliedRelationshipsStrategy;
import com.structurizr.model.ImpliedRelationshipsStrategy;

import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.Set;

final class ImpliedRelationshipsParser extends AbstractParser {

private static final String GRAMMAR = "!impliedRelationships <true|false>";
private static final String GRAMMAR = "!impliedRelationships <true|false|fqcn>";

private static final int FLAG_INDEX = 1;
private static final Set<String> BUILT_IN_IMPLIED_RELATIONSHIPS_STRATEGIES = Set.of(
"com.structurizr.model.DefaultImpliedRelationshipsStrategy",
"com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy",
"com.structurizr.model.CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy"
);

private static final int OPTION_INDEX = 1;
private static final String TRUE = "true";
private static final String FALSE = "false";

void parse(DslContext context, Tokens tokens) {
// impliedRelationships <true|false>
void parse(DslContext context, Tokens tokens, File dslFile, boolean restricted) {
// impliedRelationships <true|false|fqcn>

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

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

if (tokens.get(FLAG_INDEX).equalsIgnoreCase(FALSE)) {
String option = tokens.get(OPTION_INDEX);

if (option.equalsIgnoreCase(FALSE)) {
context.getWorkspace().getModel().setImpliedRelationshipsStrategy(new DefaultImpliedRelationshipsStrategy());
} else {
} else if (option.equalsIgnoreCase(TRUE)) {
context.getWorkspace().getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy());
} else {
if (restricted) {
if (!BUILT_IN_IMPLIED_RELATIONSHIPS_STRATEGIES.contains(option)) {
throw new RuntimeException("The implied relationships strategy " + option + " is not available when the DSL parser is running in restricted mode");
}
}

try {
Class impliedRelationshipsStrategyClass = context.loadClass(option, dslFile);
Constructor constructor = impliedRelationshipsStrategyClass.getDeclaredConstructor();
ImpliedRelationshipsStrategy impliedRelationshipsStrategy = (ImpliedRelationshipsStrategy)constructor.newInstance();
context.getWorkspace().getModel().setImpliedRelationshipsStrategy(impliedRelationshipsStrategy);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Error loading implied relationships strategy: " + option + " was not found");
} catch (Exception e) {
throw new RuntimeException("Error loading implied relationships strategy: " + e.getMessage());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ void parse(List<String> lines, File dslFile, boolean include) throws Structurizr
startContext(new WorkspaceDslContext());
parsedTokens.add(WORKSPACE_TOKEN);
} else if (IMPLIED_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) || IMPLIED_RELATIONSHIPS_TOKEN.substring(1).equalsIgnoreCase(firstToken)) {
new ImpliedRelationshipsParser().parse(getContext(), tokens);
new ImpliedRelationshipsParser().parse(getContext(), tokens, dslFile, restricted);

} else if (NAME_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) {
new WorkspaceParser().parseName(getContext(), tokens);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.junit.jupiter.api.Test;

import java.io.File;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

Expand All @@ -12,35 +14,69 @@ class ImpliedRelationshipsParserTests extends AbstractTests {
@Test
void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() {
try {
parser.parse(context(), tokens("!impliedRelationships", "boolean", "extra"));
parser.parse(context(), tokens("!impliedRelationships", "boolean", "extra"), null, false);
fail();
} catch (Exception e) {
assertEquals("Too many tokens, expected: !impliedRelationships <true|false>", e.getMessage());
assertEquals("Too many tokens, expected: !impliedRelationships <true|false|fqcn>", e.getMessage());
}
}

@Test
void test_parse_ThrowsAnException_WhenNoFlagIsSpecified() {
try {
parser.parse(context(), tokens("!impliedRelationships"));
parser.parse(context(), tokens("!impliedRelationships"), null, false);
fail();
} catch (Exception e) {
assertEquals("Expected: !impliedRelationships <true|false>", e.getMessage());
assertEquals("Expected: !impliedRelationships <true|false|fqcn>", e.getMessage());
}
}

@Test
void test_parse_SetsTheStrategy_WhenFalseIsSpecified() {
parser.parse(context(), tokens("!impliedRelationships", "false"));
parser.parse(context(), tokens("!impliedRelationships", "false"), null, false);

assertEquals("com.structurizr.model.DefaultImpliedRelationshipsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName());
}

@Test
void test_parse_SetsTheStrategy_WhenTrueIsSpecified() {
parser.parse(context(), tokens("!impliedRelationships", "true"));
parser.parse(context(), tokens("!impliedRelationships", "true"), null, false);

assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName());
}

@Test
void test_parse_SetsTheStrategy_WhenABuiltInStrategyIsUsedInUnrestrictedMode() {
parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File("."), false);

assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName());
}

@Test
void test_parse_SetsTheStrategy_WhenABuiltInStrategyIsUsedInRestrictedMode() {
parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File("."), true);

assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName());
}

@Test
void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInRestrictedMode() {
try {
parser.parse(context(), tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File("."), true);
fail();
} catch (Exception e) {
assertEquals("The implied relationships strategy com.example.CustomImpliedRelationshipsStrategy is not available when the DSL parser is running in restricted mode", e.getMessage());
}
}

@Test
void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInUnrestrictedModeButCannotBeLoaded() {
try {
parser.parse(context(), tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File("."), false);
fail();
} catch (Exception e) {
assertEquals("Error loading implied relationships strategy: com.example.CustomImpliedRelationshipsStrategy was not found", e.getMessage());
}
}

}
4 changes: 4 additions & 0 deletions structurizr-dsl/src/test/resources/dsl/test.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ workspace "Name" "Description" {
// single line comment

model {
!impliedRelationships false
!impliedRelationships "com.structurizr.model.CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy"
!impliedRelationships true

properties {
"Name" "Value"
}
Expand Down

0 comments on commit 1366d3d

Please sign in to comment.