Skip to content
This repository was archived by the owner on Mar 28, 2026. It is now read-only.

Commit eb81121

Browse files
Adds support for element icons being specified as filenames (rather than full URLs) in themes.
1 parent 190c631 commit eb81121

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

structurizr-client/src/com/structurizr/view/ThemeUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.fasterxml.jackson.databind.SerializationFeature;
77
import com.structurizr.Workspace;
88
import com.structurizr.io.WorkspaceWriterException;
9+
import com.structurizr.util.StringUtils;
910
import org.apache.hc.client5.http.classic.methods.HttpGet;
1011
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
1112
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
@@ -79,6 +80,21 @@ public static void loadThemes(Workspace workspace) throws Exception {
7980
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
8081

8182
Theme theme = objectMapper.readValue(json, Theme.class);
83+
String baseUrl = url.substring(0, url.lastIndexOf('/') + 1);
84+
85+
for (ElementStyle elementStyle : theme.getElements()) {
86+
String icon = elementStyle.getIcon();
87+
if (!StringUtils.isNullOrEmpty(icon)) {
88+
if (icon.startsWith("http")) {
89+
// okay, image served over HTTP
90+
} else if (icon.startsWith("data:image")) {
91+
// also okay, data URI
92+
} else {
93+
// convert the relative icon filename into a full URL
94+
elementStyle.setIcon(baseUrl + icon);
95+
}
96+
}
97+
}
8298

8399
workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(theme);
84100
}

structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception {
3939
assertNotNull(style);
4040
assertEquals("#d6242d", style.getStroke());
4141
assertEquals("#d6242d", style.getColor());
42-
assertNotNull(style.getIcon());
42+
assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Alexa-For-Business_light-bg@4x.png", style.getIcon());
4343
}
4444

4545
@Test
@@ -129,4 +129,24 @@ void findRelationshipStyle_WithThemes() {
129129
assertEquals(Integer.valueOf(100), style.getOpacity());
130130
}
131131

132+
@Test
133+
void loadThemes_ReplacesRelativeIconReferences() throws Exception {
134+
Workspace workspace = new Workspace("Name", "Description");
135+
SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name");
136+
softwareSystem.addTags("Amazon Web Services - Alexa For Business");
137+
workspace.getViews().getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services-2020.04.30/theme.json");
138+
139+
ThemeUtils.loadThemes(workspace);
140+
141+
// there should still be zero styles in the workspace
142+
assertEquals(0, workspace.getViews().getConfiguration().getStyles().getElements().size());
143+
144+
// but we should be able to find a style included in the theme
145+
ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem);
146+
assertNotNull(style);
147+
assertEquals("#d6242d", style.getStroke());
148+
assertEquals("#d6242d", style.getColor());
149+
assertEquals("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services-2020.04.30/Alexa-For-Business_light-bg@4x.png", style.getIcon());
150+
}
151+
132152
}

0 commit comments

Comments
 (0)