diff --git a/docs/changelog.md b/docs/changelog.md index 23ba3ce..445cb7a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.33.0 (unreleased) - DSL identifiers (if present) will now be loaded when extending a JSON workspace (see https://github.com/structurizr/dsl/discussions/328). +- Adds a `context` variable to inline/external scripts (see https://github.com/structurizr/dsl/issues/332). - Fixes https://github.com/structurizr/dsl/issues/324 (Groups with no curly braces breaks diagrams). ## 1.32.0 (28th July 2023) diff --git a/docs/language-reference.md b/docs/language-reference.md index 5233b64..1f297a7 100644 --- a/docs/language-reference.md +++ b/docs/language-reference.md @@ -360,6 +360,7 @@ Scripts can be used at any point in the DSL. The following variables are available from scripts: +- `context`: a [StructurizrDslScriptContext](https://github.com/structurizr/dsl/blob/master/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java) object with contextual information - `workspace`: the [Workspace](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/Workspace.java) object - `element`: the current [Element](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/model/Element.java) object, if the script is used within the scope of an element - `relationship`: the current [Relationship](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/model/Relationship.java) object, if the script is used within the scope of a relationship diff --git a/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java b/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java index 5526bf1..53305c1 100644 --- a/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java +++ b/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java @@ -7,13 +7,11 @@ class ExternalScriptDslContext extends ScriptDslContext { - private final File dslFile; private final String filename; ExternalScriptDslContext(DslContext parentContext, File dslFile, String filename) { - super(parentContext); + super(parentContext, dslFile); - this.dslFile = dslFile; this.filename = filename; } diff --git a/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java b/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java index 20740ab..72803e6 100644 --- a/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java +++ b/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -19,8 +20,8 @@ class InlineScriptDslContext extends ScriptDslContext { SUPPORTED_LANGUAGES.put("ruby", "rb"); } - InlineScriptDslContext(DslContext parentContext, String language) { - super(parentContext); + InlineScriptDslContext(DslContext parentContext, File dslFile, String language) { + super(parentContext, dslFile); this.language = language.toLowerCase(); } diff --git a/src/main/java/com/structurizr/dsl/ScriptDslContext.java b/src/main/java/com/structurizr/dsl/ScriptDslContext.java index 19928a3..d066bbc 100644 --- a/src/main/java/com/structurizr/dsl/ScriptDslContext.java +++ b/src/main/java/com/structurizr/dsl/ScriptDslContext.java @@ -6,12 +6,14 @@ import javax.script.Bindings; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; +import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; abstract class ScriptDslContext extends DslContext { + private static final String CONTEXT_VARIABLE_NAME = "context"; private static final String WORKSPACE_VARIABLE_NAME = "workspace"; private static final String VIEW_VARIABLE_NAME = "view"; private static final String ELEMENT_VARIABLE_NAME = "element"; @@ -19,10 +21,13 @@ abstract class ScriptDslContext extends DslContext { private final DslContext parentContext; + protected final File dslFile; + private final Map parameters = new HashMap<>(); - ScriptDslContext(DslContext parentContext) { + ScriptDslContext(DslContext parentContext, File dslFile) { this.parentContext = parentContext; + this.dslFile = dslFile; } void addParameter(String name, String value) { @@ -54,6 +59,10 @@ void run(DslContext context, String extension, List lines) throws Except } } + // bind a context object + StructurizrDslScriptContext scriptContext = new StructurizrDslScriptContext(dslFile, getWorkspace(), parameters); + bindings.put(CONTEXT_VARIABLE_NAME, scriptContext); + // and any custom parameters for (String name : parameters.keySet()) { bindings.put(name, parameters.get(name)); diff --git a/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 186df51..98ebe7b 100644 --- a/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -860,7 +860,7 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio ScriptParser scriptParser = new ScriptParser(); if (scriptParser.isInlineScript(tokens)) { String language = scriptParser.parseInline(tokens.withoutContextStartToken()); - startContext(new InlineScriptDslContext(getContext(), language)); + startContext(new InlineScriptDslContext(getContext(), dslFile, language)); } else { String filename = scriptParser.parseExternal(tokens.withoutContextStartToken()); startContext(new ExternalScriptDslContext(getContext(), dslFile, filename)); diff --git a/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java b/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java new file mode 100644 index 0000000..737c6ed --- /dev/null +++ b/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java @@ -0,0 +1,76 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.util.StringUtils; + +import java.io.File; +import java.util.Map; + +/** + * Used to pass contextual information to DSL scripts when they are executed. + */ +public class StructurizrDslScriptContext { + + private final File dslFile; + private final Workspace workspace; + private final Map parameters; + + /** + * Creates a new instance. + * + * @param dslFile a reference to the DSL file that loaded the script + * @param workspace the workspace + * @param parameters a map of name/value pairs representing parameters + */ + public StructurizrDslScriptContext(File dslFile, Workspace workspace, Map parameters) { + this.dslFile = dslFile; + this.workspace = workspace; + this.parameters = parameters; + } + + /** + * Gets a reference to the DSL file that initiated this script context. + * + * @return a File instance + */ + public File getDslFile() { + return dslFile; + } + + /** + * Gets the current workspace. + * + * @return a Workspace instance + */ + public Workspace getWorkspace() { + return workspace; + } + + /** + * Gets the named parameter. + * + * @param name the parameter name + * @return the parameter value (null if unset) + */ + public String getParameter(String name) { + return parameters.get(name); + } + + /** + * Gets the named parameter, with a default value if unset. + * + * @param name the parameter name + * @param defaultValue the default value + * @return the parameter value, or defaultValue if unset + */ + public String getParameter(String name, String defaultValue) { + String value = parameters.get(name); + + if (StringUtils.isNullOrEmpty(value)) { + value = defaultValue; + } + + return value; + } + +} \ No newline at end of file diff --git a/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java b/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java index 2d82414..6b27753 100644 --- a/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java +++ b/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java @@ -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; @@ -10,7 +12,7 @@ class InlineScriptDslContextTests extends AbstractTests { @Test void test_end_ThrowsAnException_WhenAnUnsupportedLanguageIsSpecified() { try { - InlineScriptDslContext context = new InlineScriptDslContext(new WorkspaceDslContext(), "java"); + InlineScriptDslContext context = new InlineScriptDslContext(new WorkspaceDslContext(), new File("workspace.dsl"), "java"); context.end(); fail(); } catch (Exception e) {