diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ColorUtils.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ColorUtils.kt index 1cababe..77027d5 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ColorUtils.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ColorUtils.kt @@ -2,8 +2,10 @@ package com.github.chriskn.structurizrextension.api.view.style import com.structurizr.view.Color -fun toValidColor(color: String): String = - if (Color.isHexColorCode(color)) { +fun toValidColor(color: String?): String? = + if (color == null) { + null + } else if (Color.isHexColorCode(color)) { color.lowercase() } else { val hexColorCode = Color.fromColorNameToHexColorCode(color) diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ElementStyleExtensions.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ElementStyleExtensions.kt deleted file mode 100644 index 1b3b6e4..0000000 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ElementStyleExtensions.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.github.chriskn.structurizrextension.api.view.style - -import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite -import com.github.chriskn.structurizrextension.internal.export.view.style.spriteFormJson -import com.github.chriskn.structurizrextension.internal.export.view.style.toJson -import com.structurizr.view.Border -import com.structurizr.view.ElementStyle -import com.structurizr.view.createElementStyleFromTag - -private const val BORDER_COLOR_PROPERTY_NAME = "c4:borderColor" -private const val SHADOWING_PROPERTY_NAME = "c4:shadowing" -private const val SHAPE_PROPERTY_NAME = "c4:shape" -private const val SPRITE_PROPERTY_NAME = "c4:sprite" -private const val LEGEND_SPRITE_PROPERTY_NAME = "c4:legendSprite" -private const val LEGEND_TEXT_PROPERTY_NAME = "c4:legendText" -private const val TECHNOLOGY_PROPERTY_NAME = "c4:technology" - -// TODO doucment - -// @JsonCreator -@Suppress("LongParameterList") -fun createElementStyle( - tag: String, - backgroundColor: String? = null, - fontColor: String? = null, - border: Border? = null, - borderWith: Int? = null, - borderColor: String? = null, - technology: String? = null, - shadowing: Boolean = false, - c4Shape: C4Shape? = null, - sprite: Sprite? = null, - legendText: String? = null, - legendSprite: Sprite? = null, -): ElementStyle { - require(tag.isNotBlank()) { "tag cannot be blank" } - val style = createElementStyleFromTag(tag) - style.backgroundColor = backgroundColor - style.fontColor = fontColor - style.border = border - style.borderWith = borderWith - style.borderColor = borderColor - style.technology = technology - style.shadowing = shadowing - style.c4Shape = c4Shape - style.sprite = sprite - style.legendSprite = legendSprite - style.legendText = legendText - return style -} - -var ElementStyle.backgroundColor: String? - get() = this.background - private set(backgroundColor) { - backgroundColor?.let { this.background = backgroundColor } - } - -var ElementStyle.fontColor: String? - get() = this.color - private set(fontColor) { - fontColor?.let { this.color = fontColor } - } - -var ElementStyle.borderWith: Int? - get() = this.strokeWidth - private set(strokeWith) { - strokeWith?.let { this.strokeWidth = strokeWith } - } - -var ElementStyle.borderColor: String? - get() = this.properties[BORDER_COLOR_PROPERTY_NAME] - private set(borderColor) { - borderColor?.let { this.addProperty(BORDER_COLOR_PROPERTY_NAME, toValidColor(borderColor)) } - } - -var ElementStyle.technology: String? - get() = this.properties[TECHNOLOGY_PROPERTY_NAME] - private set(technology) { - technology?.let { this.addProperty(TECHNOLOGY_PROPERTY_NAME, technology) } - } - -var ElementStyle.shadowing: Boolean - get() = this.properties[SHADOWING_PROPERTY_NAME] == "true" - private set(shadowing) { - if (shadowing) { - this.addProperty(SHADOWING_PROPERTY_NAME, "true") - } - } - -var ElementStyle.c4Shape: C4Shape? - get() = this.properties[SHAPE_PROPERTY_NAME]?.let { C4Shape.valueOf(it) } - private set(c4Shape) { - c4Shape?.let { this.addProperty(SHAPE_PROPERTY_NAME, c4Shape.toString()) } - } - -var ElementStyle.sprite: Sprite? - get() = this.properties[SPRITE_PROPERTY_NAME]?.let { spriteFormJson(it) } - private set(sprite) { - sprite?.let { this.addProperty(SPRITE_PROPERTY_NAME, it.toJson()) } - } - -var ElementStyle.legendSprite: Sprite? - get() = this.properties[LEGEND_SPRITE_PROPERTY_NAME]?.let { spriteFormJson(it) } - private set(legendSprite) { - legendSprite?.let { this.addProperty(LEGEND_SPRITE_PROPERTY_NAME, it.toJson()) } - } - -var ElementStyle.legendText: String? - get() = this.properties[LEGEND_TEXT_PROPERTY_NAME] - private set(legendText) { - legendText?.let { this.addProperty(LEGEND_TEXT_PROPERTY_NAME, legendText) } - } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt index 6fe0976..0a1ef80 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt @@ -1,15 +1,52 @@ package com.github.chriskn.structurizrextension.api.view.style -import com.structurizr.view.ElementStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.PersonStyle +import com.github.chriskn.structurizrextension.internal.export.view.style.boundaryStyleFromJson +import com.github.chriskn.structurizrextension.internal.export.view.style.elementStyleFromJson +import com.github.chriskn.structurizrextension.internal.export.view.style.personStyleFromJson +import com.github.chriskn.structurizrextension.internal.export.view.style.toJson import com.structurizr.view.ViewSet +import com.structurizr.view.clearBoundaryStyles +import com.structurizr.view.clearElementStyles +import com.structurizr.view.clearPersonStyles fun ViewSet.addElementStyle(elementStyle: ElementStyle) { - this.configuration.styles.add(elementStyle) + this.configuration.addProperty("$ELEMENT_STYLE_PROPERTY_NAME_PREFIX:${elementStyle.tag}", elementStyle.toJson()) } fun ViewSet.getElementStyles(): List = - this.configuration.styles.elements.toList() + this.configuration.properties + .filter { it.key.startsWith(ELEMENT_STYLE_PROPERTY_NAME_PREFIX) } + .map { elementStyleFromJson(it.value) } internal fun ViewSet.clearElementStyles() { - this.configuration.styles.elements.clear() + this.configuration.clearElementStyles() +} + +fun ViewSet.addBoundaryStyle(boundaryStyle: BoundaryStyle) { + this.configuration.addProperty("$BOUNDARY_STYLE_PROPERTY_NAME_PREFIX:${boundaryStyle.tag}", boundaryStyle.toJson()) +} + +fun ViewSet.getBoundaryStyles(): List = + this.configuration.properties + .filter { it.key.startsWith(BOUNDARY_STYLE_PROPERTY_NAME_PREFIX) } + .map { boundaryStyleFromJson(it.value) } + +internal fun ViewSet.clearBoundaryStyles() { + this.configuration.clearBoundaryStyles() +} + +fun ViewSet.addPersonStyle(personStyle: PersonStyle) { + this.configuration.addProperty("$PERSON_STYLE_PROPERTY_NAME_PREFIX:${personStyle.tag}", personStyle.toJson()) +} + +fun ViewSet.getPersonStyles(): List = + this.configuration.properties + .filter { it.key.startsWith(PERSON_STYLE_PROPERTY_NAME_PREFIX) } + .map { personStyleFromJson(it.value) } + +internal fun ViewSet.clearPersonStyles() { + this.configuration.clearPersonStyles() } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt index 37b685b..5ad4ab0 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt @@ -1,11 +1,17 @@ package com.github.chriskn.structurizrextension.api.view.style +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.PersonStyle +import com.github.chriskn.structurizrextension.internal.export.view.style.boundaryStyleFromJson import com.github.chriskn.structurizrextension.internal.export.view.style.elementStyleFromJson +import com.github.chriskn.structurizrextension.internal.export.view.style.personStyleFromJson import com.github.chriskn.structurizrextension.internal.export.view.style.toJson -import com.structurizr.view.ElementStyle import com.structurizr.view.View -private const val ELEMENT_STYLE_PROPERTY_NAME_PREFIX = "c4:elementStyle" +internal const val ELEMENT_STYLE_PROPERTY_NAME_PREFIX = "c4:elementStyle" +internal const val BOUNDARY_STYLE_PROPERTY_NAME_PREFIX = "c4:boundaryStyle" +internal const val PERSON_STYLE_PROPERTY_NAME_PREFIX = "c4:personStyle" fun View.addElementStyle(elementStyle: ElementStyle) { this.addProperty("$ELEMENT_STYLE_PROPERTY_NAME_PREFIX:${elementStyle.tag}", elementStyle.toJson()) @@ -15,3 +21,21 @@ fun View.getElementStyles(): List = this.properties .filter { it.key.startsWith(ELEMENT_STYLE_PROPERTY_NAME_PREFIX) } .map { elementStyleFromJson(it.value) } + +fun View.addBoundaryStyle(boundaryStyle: BoundaryStyle) { + this.addProperty("$BOUNDARY_STYLE_PROPERTY_NAME_PREFIX:${boundaryStyle.tag}", boundaryStyle.toJson()) +} + +fun View.getBoundaryStyles(): List = + this.properties + .filter { it.key.startsWith(BOUNDARY_STYLE_PROPERTY_NAME_PREFIX) } + .map { boundaryStyleFromJson(it.value) } + +fun View.addPersonStyle(personStyle: PersonStyle) { + this.addProperty("$PERSON_STYLE_PROPERTY_NAME_PREFIX:${personStyle.tag}", personStyle.toJson()) +} + +fun View.getPersonStyles(): List = + this.properties + .filter { it.key.startsWith(PERSON_STYLE_PROPERTY_NAME_PREFIX) } + .map { personStyleFromJson(it.value) } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/BoundaryStyle.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/BoundaryStyle.kt new file mode 100644 index 0000000..a9441d2 --- /dev/null +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/BoundaryStyle.kt @@ -0,0 +1,87 @@ +package com.github.chriskn.structurizrextension.api.view.style.styles + +import com.github.chriskn.structurizrextension.api.view.style.C4Shape +import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite +import com.github.chriskn.structurizrextension.api.view.style.toValidColor +import com.structurizr.view.Border + +// TODO doucment + +class BoundaryStyle : C4PumlStyle { + + override val tag: String + override val backgroundColor: String? + override val fontColor: String? + override val border: Border? + override val borderWith: Int? + override val borderColor: String? + override val shadowing: Boolean + override val c4Shape: C4Shape? + override val sprite: Sprite? + override val legendText: String? + override val legendSprite: Sprite? + + @Suppress("LongParameterList") + constructor( + tag: String, + backgroundColor: String? = null, + fontColor: String? = null, + border: Border? = null, + borderWith: Int? = null, + borderColor: String? = null, + shadowing: Boolean = false, + c4Shape: C4Shape? = null, + sprite: Sprite? = null, + legendText: String? = null, + legendSprite: Sprite? = null + ) { + require(tag.isNotBlank()) { "tag cannot be blank" } + this.tag = tag + this.backgroundColor = toValidColor(backgroundColor) + this.fontColor = toValidColor(fontColor) + this.border = border + this.borderWith = borderWith + this.borderColor = toValidColor(borderColor) + this.shadowing = shadowing + this.c4Shape = c4Shape + this.sprite = sprite + this.legendText = legendText + this.legendSprite = legendSprite + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as BoundaryStyle + + if (tag != other.tag) return false + if (backgroundColor != other.backgroundColor) return false + if (fontColor != other.fontColor) return false + if (border != other.border) return false + if (borderWith != other.borderWith) return false + if (borderColor != other.borderColor) return false + if (shadowing != other.shadowing) return false + if (c4Shape != other.c4Shape) return false + if (sprite != other.sprite) return false + if (legendText != other.legendText) return false + if (legendSprite != other.legendSprite) return false + + return true + } + + override fun hashCode(): Int { + var result = tag.hashCode() + result = 31 * result + (backgroundColor?.hashCode() ?: 0) + result = 31 * result + (fontColor?.hashCode() ?: 0) + result = 31 * result + (border?.hashCode() ?: 0) + result = 31 * result + (borderWith ?: 0) + result = 31 * result + (borderColor?.hashCode() ?: 0) + result = 31 * result + shadowing.hashCode() + result = 31 * result + (c4Shape?.hashCode() ?: 0) + result = 31 * result + (sprite?.hashCode() ?: 0) + result = 31 * result + (legendText?.hashCode() ?: 0) + result = 31 * result + (legendSprite?.hashCode() ?: 0) + return result + } +} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/C4PumlStyle.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/C4PumlStyle.kt new file mode 100644 index 0000000..3f366f1 --- /dev/null +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/C4PumlStyle.kt @@ -0,0 +1,20 @@ +package com.github.chriskn.structurizrextension.api.view.style.styles + +import com.github.chriskn.structurizrextension.api.view.style.C4Shape +import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite +import com.structurizr.view.Border + +// TODO move to internal package +interface C4PumlStyle { + val tag: String + val backgroundColor: String? + val fontColor: String? + val border: Border? + val borderWith: Int? + val borderColor: String? + val shadowing: Boolean + val c4Shape: C4Shape? + val sprite: Sprite? + val legendText: String? + val legendSprite: Sprite? +} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/ElementStyle.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/ElementStyle.kt new file mode 100644 index 0000000..6796d2c --- /dev/null +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/ElementStyle.kt @@ -0,0 +1,92 @@ +package com.github.chriskn.structurizrextension.api.view.style.styles + +import com.github.chriskn.structurizrextension.api.view.style.C4Shape +import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite +import com.github.chriskn.structurizrextension.api.view.style.toValidColor +import com.structurizr.view.Border + +// TODO document + +class ElementStyle : C4PumlStyle { + + override val tag: String + override val backgroundColor: String? + override val fontColor: String? + override val border: Border? + override val borderWith: Int? + override val borderColor: String? + override val shadowing: Boolean + override val c4Shape: C4Shape? + override val sprite: Sprite? + override val legendText: String? + override val legendSprite: Sprite? + val technology: String? + + @Suppress("LongParameterList") + constructor( + tag: String, + backgroundColor: String? = null, + fontColor: String? = null, + border: Border? = null, + borderWith: Int? = null, + borderColor: String? = null, + shadowing: Boolean = false, + c4Shape: C4Shape? = null, + sprite: Sprite? = null, + legendText: String? = null, + legendSprite: Sprite? = null, + technology: String? = null + ) { + require(tag.isNotBlank()) { "tag cannot be blank" } + this.tag = tag + this.backgroundColor = toValidColor(backgroundColor) + this.fontColor = toValidColor(fontColor) + this.border = border + this.borderWith = borderWith + this.borderColor = toValidColor(borderColor) + this.shadowing = shadowing + this.c4Shape = c4Shape + this.sprite = sprite + this.legendText = legendText + this.legendSprite = legendSprite + this.technology = technology + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ElementStyle + + if (tag != other.tag) return false + if (backgroundColor != other.backgroundColor) return false + if (fontColor != other.fontColor) return false + if (border != other.border) return false + if (borderWith != other.borderWith) return false + if (borderColor != other.borderColor) return false + if (shadowing != other.shadowing) return false + if (c4Shape != other.c4Shape) return false + if (sprite != other.sprite) return false + if (legendText != other.legendText) return false + if (legendSprite != other.legendSprite) return false + if (technology != other.technology) return false + + return true + } + + override fun hashCode(): Int { + var result = tag.hashCode() + result = 31 * result + (backgroundColor?.hashCode() ?: 0) + result = 31 * result + (fontColor?.hashCode() ?: 0) + result = 31 * result + (border?.hashCode() ?: 0) + result = 31 * result + (borderWith ?: 0) + result = 31 * result + (borderColor?.hashCode() ?: 0) + result = 31 * result + shadowing.hashCode() + result = 31 * result + (c4Shape?.hashCode() ?: 0) + result = 31 * result + (sprite?.hashCode() ?: 0) + result = 31 * result + (legendText?.hashCode() ?: 0) + result = 31 * result + (legendSprite?.hashCode() ?: 0) + result = 31 * result + (technology?.hashCode() ?: 0) + return result + } +} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/PersonStyle.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/PersonStyle.kt new file mode 100644 index 0000000..158ff82 --- /dev/null +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/styles/PersonStyle.kt @@ -0,0 +1,87 @@ +package com.github.chriskn.structurizrextension.api.view.style.styles + +import com.github.chriskn.structurizrextension.api.view.style.C4Shape +import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite +import com.github.chriskn.structurizrextension.api.view.style.toValidColor +import com.structurizr.view.Border + +// TODO document + +class PersonStyle : C4PumlStyle { + + override val tag: String + override val backgroundColor: String? + override val fontColor: String? + override val border: Border? + override val borderWith: Int? + override val borderColor: String? + override val shadowing: Boolean + override val c4Shape: C4Shape? + override val sprite: Sprite? + override val legendText: String? + override val legendSprite: Sprite? + + @Suppress("LongParameterList") + constructor( + tag: String, + backgroundColor: String? = null, + fontColor: String? = null, + border: Border? = null, + borderWith: Int? = null, + borderColor: String? = null, + shadowing: Boolean = false, + c4Shape: C4Shape? = null, + sprite: Sprite? = null, + legendText: String? = null, + legendSprite: Sprite? = null + ) { + require(tag.isNotBlank()) { "tag cannot be blank" } + this.tag = tag + this.backgroundColor = toValidColor(backgroundColor) + this.fontColor = toValidColor(fontColor) + this.border = border + this.borderWith = borderWith + this.borderColor = toValidColor(borderColor) + this.shadowing = shadowing + this.c4Shape = c4Shape + this.sprite = sprite + this.legendText = legendText + this.legendSprite = legendSprite + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as PersonStyle + + if (tag != other.tag) return false + if (backgroundColor != other.backgroundColor) return false + if (fontColor != other.fontColor) return false + if (border != other.border) return false + if (borderWith != other.borderWith) return false + if (borderColor != other.borderColor) return false + if (shadowing != other.shadowing) return false + if (c4Shape != other.c4Shape) return false + if (sprite != other.sprite) return false + if (legendText != other.legendText) return false + if (legendSprite != other.legendSprite) return false + + return true + } + + override fun hashCode(): Int { + var result = tag.hashCode() + result = 31 * result + (backgroundColor?.hashCode() ?: 0) + result = 31 * result + (fontColor?.hashCode() ?: 0) + result = 31 * result + (border?.hashCode() ?: 0) + result = 31 * result + (borderWith ?: 0) + result = 31 * result + (borderColor?.hashCode() ?: 0) + result = 31 * result + shadowing.hashCode() + result = 31 * result + (c4Shape?.hashCode() ?: 0) + result = 31 * result + (sprite?.hashCode() ?: 0) + result = 31 * result + (legendText?.hashCode() ?: 0) + result = 31 * result + (legendSprite?.hashCode() ?: 0) + return result + } +} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/ExtendedC4PlantUMLExporter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/ExtendedC4PlantUMLExporter.kt index c18f699..c551526 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/ExtendedC4PlantUMLExporter.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/ExtendedC4PlantUMLExporter.kt @@ -7,11 +7,11 @@ import com.github.chriskn.structurizrextension.internal.export.view.DeploymentVi import com.github.chriskn.structurizrextension.internal.export.view.DynamicViewExporter import com.github.chriskn.structurizrextension.internal.export.view.SystemViewExporter import com.github.chriskn.structurizrextension.internal.export.writer.BoundaryWriter -import com.github.chriskn.structurizrextension.internal.export.writer.ElementStyleWriter import com.github.chriskn.structurizrextension.internal.export.writer.ElementWriter import com.github.chriskn.structurizrextension.internal.export.writer.FooterWriter import com.github.chriskn.structurizrextension.internal.export.writer.HeaderWriter import com.github.chriskn.structurizrextension.internal.export.writer.PropertyWriter +import com.github.chriskn.structurizrextension.internal.export.writer.StyleWriter import com.github.chriskn.structurizrextension.internal.export.writer.relationship.RelationshipWriter import com.structurizr.export.AbstractDiagramExporter import com.structurizr.export.Diagram @@ -33,7 +33,7 @@ import com.structurizr.view.SystemLandscapeView @Suppress("TooManyFunctions") internal class ExtendedC4PlantUMLExporter : AbstractDiagramExporter() { - private val elementStyleWriter = ElementStyleWriter + private val elementStyleWriter = StyleWriter private val boundaryWriter = BoundaryWriter private val footerWriter = FooterWriter private val headerWriter = HeaderWriter(elementStyleWriter) diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ComponentViewBoundaryExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ComponentViewBoundaryExtension.kt new file mode 100644 index 0000000..21d442e --- /dev/null +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ComponentViewBoundaryExtension.kt @@ -0,0 +1,20 @@ +package com.github.chriskn.structurizrextension.internal.export.view + +import com.github.chriskn.structurizrextension.api.view.showExternalContainerBoundaries +import com.github.chriskn.structurizrextension.internal.export.idOf +import com.structurizr.model.Component +import com.structurizr.view.ComponentView +import com.structurizr.view.ElementView + +internal fun ComponentView.getBoundaryContainer() = + if (this.showExternalContainerBoundaries) { + this.elements + .asSequence() + .map { obj: ElementView -> obj.element } + .filterIsInstance() + .map { it.container } + .toSet() + .sortedBy { idOf(it) } + } else { + emptyList() + } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ComponentViewExporter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ComponentViewExporter.kt index 6f4d1a9..88afd49 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ComponentViewExporter.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ComponentViewExporter.kt @@ -5,11 +5,8 @@ import com.github.chriskn.structurizrextension.internal.export.ExtendedC4PlantUM import com.github.chriskn.structurizrextension.internal.export.idOf import com.structurizr.export.Diagram import com.structurizr.export.IndentingWriter -import com.structurizr.model.Component -import com.structurizr.model.Container import com.structurizr.view.ComponentView import com.structurizr.view.ElementView -import com.structurizr.view.ModelView internal class ComponentViewExporter( private val c4PlantUMLExporter: ExtendedC4PlantUMLExporter, @@ -58,7 +55,7 @@ internal class ComponentViewExporter( writer: IndentingWriter, sortedElements: List ): List { - val boundaryContainers = getBoundaryContainer(view) + val boundaryContainers = view.getBoundaryContainer() for (container in boundaryContainers) { c4PlantUMLExporter.startContainerBoundary(view, container, writer) @@ -71,13 +68,4 @@ internal class ComponentViewExporter( } return view.elements.filter { it.element.parent in boundaryContainers } } - - private fun getBoundaryContainer(view: ModelView): List = - view.elements - .asSequence() - .map { obj: ElementView -> obj.element } - .filterIsInstance() - .map { it.container } - .toSet() - .sortedBy { idOf(it) } } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ContainerViewBoundaryExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ContainerViewBoundaryExtension.kt new file mode 100644 index 0000000..e28a5fc --- /dev/null +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ContainerViewBoundaryExtension.kt @@ -0,0 +1,19 @@ +package com.github.chriskn.structurizrextension.internal.export.view + +import com.github.chriskn.structurizrextension.api.view.showExternalSoftwareSystemBoundaries +import com.github.chriskn.structurizrextension.internal.export.idOf +import com.structurizr.model.Container +import com.structurizr.view.ContainerView + +internal fun ContainerView.getBoundarySystems() = + if (this.showExternalSoftwareSystemBoundaries) { + this.elements + .asSequence() + .map { it.element } + .filterIsInstance() + .map { it.softwareSystem } + .toSet() + .sortedBy { idOf(it) } + } else { + emptyList() + } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ContainerViewExporter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ContainerViewExporter.kt index c7f76cc..994d00b 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ContainerViewExporter.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/ContainerViewExporter.kt @@ -5,11 +5,8 @@ import com.github.chriskn.structurizrextension.internal.export.ExtendedC4PlantUM import com.github.chriskn.structurizrextension.internal.export.idOf import com.structurizr.export.Diagram import com.structurizr.export.IndentingWriter -import com.structurizr.model.Container -import com.structurizr.model.SoftwareSystem import com.structurizr.view.ContainerView import com.structurizr.view.ElementView -import com.structurizr.view.ModelView internal class ContainerViewExporter( private val c4PlantUMLExporter: ExtendedC4PlantUMLExporter, @@ -21,14 +18,14 @@ internal class ContainerViewExporter( val sortedElements = view.elements.sortedBy { idOf(it.element) } - val boundarySoftwareSystems: List = if (view.showExternalSoftwareSystemBoundaries) { + val elementsInBoundaries: List = if (view.showExternalSoftwareSystemBoundaries) { writeElementsInSoftwareSystemBoundaries(view, writer, sortedElements) } else { emptyList() } writeElementsOutsideBoundaries( - elementsOutsideBoundaries = sortedElements - boundarySoftwareSystems.toSet(), + elementsOutsideBoundaries = sortedElements - elementsInBoundaries.toSet(), view, writer ) @@ -57,7 +54,7 @@ internal class ContainerViewExporter( writer: IndentingWriter, sortedElements: List ): List { - val boundarySoftwareSystems = getBoundarySoftwareSystemViews(view) + val boundarySoftwareSystems = view.getBoundarySystems() for (softwareSystem in boundarySoftwareSystems) { c4PlantUMLExporter.startSoftwareSystemBoundary(view, softwareSystem, writer) for (elementView in sortedElements) { @@ -69,12 +66,4 @@ internal class ContainerViewExporter( } return view.elements.filter { it.element.parent in boundarySoftwareSystems } } - - private fun getBoundarySoftwareSystemViews(view: ModelView): List = view.elements - .asSequence() - .map { it.element } - .filterIsInstance() - .map { it.softwareSystem } - .toSet() - .sortedBy { idOf(it) } } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/style/ElementStyleJsonExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/style/ElementStyleJsonExtension.kt index 60d8abf..9a5feca 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/style/ElementStyleJsonExtension.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/style/ElementStyleJsonExtension.kt @@ -2,10 +2,20 @@ package com.github.chriskn.structurizrextension.internal.export.view.style import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue -import com.structurizr.view.ElementStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.PersonStyle private val mapper = jacksonObjectMapper() internal fun ElementStyle.toJson(): String = mapper.writeValueAsString(this) internal fun elementStyleFromJson(json: String): ElementStyle = mapper.readValue(json) + +internal fun BoundaryStyle.toJson(): String = mapper.writeValueAsString(this) + +internal fun boundaryStyleFromJson(json: String): BoundaryStyle = mapper.readValue(json) + +internal fun PersonStyle.toJson(): String = mapper.writeValueAsString(this) + +internal fun personStyleFromJson(json: String): PersonStyle = mapper.readValue(json) diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/style/SpriteJsonExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/style/SpriteJsonExtension.kt deleted file mode 100644 index 7ba6556..0000000 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/view/style/SpriteJsonExtension.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.chriskn.structurizrextension.internal.export.view.style - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite - -private val mapper = jacksonObjectMapper() - -internal fun Sprite.toJson(): String = mapper.writeValueAsString(this) - -internal fun spriteFormJson(json: String): Sprite = mapper.readValue(json) diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/BoundaryWriter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/BoundaryWriter.kt index fe1f045..749bbcf 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/BoundaryWriter.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/BoundaryWriter.kt @@ -8,6 +8,7 @@ import com.github.chriskn.structurizrextension.internal.export.idOf import com.structurizr.export.IndentingWriter import com.structurizr.model.Container import com.structurizr.model.DeploymentNode +import com.structurizr.model.ModelItem import com.structurizr.model.SoftwareSystem import com.structurizr.view.DynamicView import com.structurizr.view.View @@ -45,7 +46,9 @@ internal object BoundaryWriter { writer: IndentingWriter ) { writer.writeLine( - "System_Boundary(${idOf(softwareSystem)}, ${softwareSystem.name}) ${determineBoundaryStartSymbol(view)}" + "System_Boundary(${idOf( + softwareSystem + )}, ${softwareSystem.name}${tagsToPlantUmlSting(softwareSystem)}) ${determineBoundaryStartSymbol(view)} " ) writer.indent() } @@ -58,10 +61,8 @@ internal object BoundaryWriter { fun startContainerBoundary(view: View, container: Container, writer: IndentingWriter) { writer.writeLine( """Container_Boundary("${ - idOf( - container - ) - }_boundary", "${container.name}") ${determineBoundaryStartSymbol(view)}""" + idOf(container) + }_boundary", "${container.name}" ${tagsToPlantUmlSting(container)})${determineBoundaryStartSymbol(view)} """ ) writer.indent() } @@ -103,4 +104,14 @@ internal object BoundaryWriter { } else { "{" } + + private fun tagsToPlantUmlSting(modelItem: ModelItem): String { + // dont add structurizr default tags + val tagList = modelItem.tagsAsSet - modelItem.defaultTags + return if (tagList.isEmpty()) { + "" + } else { + """, ${'$'}tags="${tagList.joinToString("+")}"""" + } + } } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementStyleWriter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementStyleWriter.kt deleted file mode 100644 index 5916e16..0000000 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementStyleWriter.kt +++ /dev/null @@ -1,141 +0,0 @@ -package com.github.chriskn.structurizrextension.internal.export.writer - -import com.github.chriskn.structurizrextension.api.view.style.C4Shape -import com.github.chriskn.structurizrextension.api.view.style.borderColor -import com.github.chriskn.structurizrextension.api.view.style.c4Shape -import com.github.chriskn.structurizrextension.api.view.style.getElementStyles -import com.github.chriskn.structurizrextension.api.view.style.legendSprite -import com.github.chriskn.structurizrextension.api.view.style.legendText -import com.github.chriskn.structurizrextension.api.view.style.shadowing -import com.github.chriskn.structurizrextension.api.view.style.sprite -import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite -import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite -import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite -import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite -import com.github.chriskn.structurizrextension.api.view.style.technology -import com.structurizr.export.IndentingWriter -import com.structurizr.model.ModelItem -import com.structurizr.view.Border.Dashed -import com.structurizr.view.Border.Dotted -import com.structurizr.view.Border.Solid -import com.structurizr.view.ElementStyle -import com.structurizr.view.ModelView - -internal object ElementStyleWriter { - - fun collectAppliedElementStyles(view: ModelView): List { - val elements: MutableSet = view.elements.map { it.element }.toMutableSet() - val usedTags = elements.map { it.tagsAsSet }.flatten().toSet() - val stylesForTags = - view - .viewSet - .configuration - .styles - .elements - .filter { usedTags.contains(it.tag) } + - view - .getElementStyles() - .filter { usedTags.contains(it.tag) } - return stylesForTags.distinctBy { it.tag } - } - - fun writeElementStyle(elementStyle: ElementStyle, writer: IndentingWriter) { - val bgColor = elementStyle.background - val fontColor = elementStyle.color - val borderColor = elementStyle.borderColor - val borderStyle = when (elementStyle.border) { - Solid -> "SolidLine()" - Dashed -> "DashedLine()" - Dotted -> "DottedLine()" - else -> null - } - val borderThickness = elementStyle.strokeWidth?.toString() - val shadow = elementStyle.shadowing - val technology = elementStyle.technology - val shapeValue = elementStyle.c4Shape - val shape = when (shapeValue) { - C4Shape.EIGHT_SIDED -> "EightSidedShape()" - C4Shape.ROUNDED_BOX -> "RoundedBoxShape()" - else -> null - } - val sprite = elementStyle.sprite?.toPlantUmlString() - val legendSprite = elementStyle.legendSprite?.toPlantUmlString() - val legendText = elementStyle.legendText - writer.writeLine( - """AddElementTag(${elementStyle.tag}${ - addIfNotNull("sprite", sprite) - }${ - addIfNotNull("bgColor", bgColor) - }${ - addIfNotNull("fontColor", fontColor) - }${ - addIfNotNull("borderColor", borderColor) - }${ - addIfNotNull("borderStyle", borderStyle) - }${ - addIfNotNull("borderThickness", borderThickness) - }${ - addIfNotNull("shadowing", shadow) - }${ - addIfNotNull("shape", shape) - }${ - addIfNotNull("techn", technology) - }${ - addIfNotNull("legendSprite", legendSprite) - }${ - addIfNotNull("legendText", legendText) - })""" - ) - } - - private fun addIfNotNull(name: String, value: Any?) = if (value != null) { - """, ${'$'}$name=$value""" - } else { - "" - } - - private fun Sprite.toPlantUmlString(): String = when (this) { - is PumlSprite -> """"${spriteString(this.name, scale, validatedColor)}"""" - is OpenIconicSprite -> """"&${spriteString(this.name, scale, validatedColor)}"""" - is ImageSprite -> { - val scaleString = scaleString(this.scale) - if (scaleString.isBlank()) { - """"$url"""" - } else { - """"$url{$scaleString}"""" - } - } - - else -> throw IllegalArgumentException("Unknown sprite type ${this::class}") - } - - private fun spriteString( - name: String, - scale: Double?, - color: String?, - ): String { - val scaleString = scaleString(scale) - val colorString = colorString(color) - return if (scaleString.isBlank() && colorString.isBlank()) { - name - } else if (colorString.isBlank()) { - "$name{$scaleString}" - } else if (scaleString.isBlank()) { - "$name{$colorString}" - } else { - "$name{$scaleString,$colorString}" - } - } - - private fun colorString(color: String?): String = if (color != null) { - "color=$color" - } else { - "" - } - - private fun scaleString(scale: Double?): String = if (scale != null) { - "scale=$scale" - } else { - "" - } -} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementWriter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementWriter.kt index 7add35d..b82c69d 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementWriter.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementWriter.kt @@ -86,7 +86,7 @@ internal class ElementWriter( val externalMarker = this.c4Location.toPlantUmlString() return """Person$externalMarker(${idOf(this)}, "$name", "${description.orEmpty()}", "${ IconRegistry.iconFileNameFor(icon).orEmpty() - }"${linkString(link)})""" + }"${tagsToPlantUmlSting(this)}${linkString(link)})""" } private fun Component.toMacro(): String { @@ -94,7 +94,7 @@ internal class ElementWriter( idOf(this) }, "$name", "${technology.orEmpty()}", "${description.orEmpty()}", "${ IconRegistry.iconFileNameFor(icon).orEmpty() - }"${linkString(link)})""" + }"${tagsToPlantUmlSting(this)}${linkString(link)})""" } private fun Location.toPlantUmlString() = if (this == Location.External) "_Ext" else "" @@ -105,7 +105,7 @@ internal class ElementWriter( return if (tagList.isEmpty()) { "" } else { - """, ${'$'}tags="${tagList.joinToString("+")}" """ + """, ${'$'}tags="${tagList.joinToString("+")}"""" } } } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/HeaderWriter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/HeaderWriter.kt index 9c77db9..c49c719 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/HeaderWriter.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/HeaderWriter.kt @@ -6,9 +6,9 @@ import com.github.chriskn.structurizrextension.api.icons.IconRegistry import com.github.chriskn.structurizrextension.api.model.icon import com.github.chriskn.structurizrextension.api.view.dynamic.renderAsSequenceDiagram import com.github.chriskn.structurizrextension.api.view.layout.LayoutRegistry -import com.github.chriskn.structurizrextension.api.view.style.legendSprite -import com.github.chriskn.structurizrextension.api.view.style.sprite import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle import com.structurizr.export.IndentingWriter import com.structurizr.model.DeploymentNode import com.structurizr.model.InteractionStyle @@ -31,19 +31,22 @@ private const val ASYNC_STYLE_ATTIRBUTES = private const val C4_PLANT_UML_STDLIB_URL = "https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master" -internal class HeaderWriter(private val elementStyleWriter: ElementStyleWriter) { +internal class HeaderWriter(private val styleWriter: StyleWriter) { private val includes = mutableSetOf() fun writeHeader(view: ModelView, writer: IndentingWriter) { includes.clear() - val elementsStyles = elementStyleWriter.collectAppliedElementStyles(view) addIconIncludeUrls(view) - addSpriteIncludeUrls(view) + val elementsStyles = styleWriter.collectAppliedElementStyles(view) + val boundaryStyles = styleWriter.collectAppliedBoundaryStyles(view) + val personStyles = styleWriter.collectAppliedPersonStyles(view) + + addSpriteIncludeUrls(elementsStyles, boundaryStyles) // Spaces in PlantUML ids can cause issues. Alternatively, id can be surrounded with double quotes writer.writeLine("@startuml(id=${view.key.replace(' ', '_')})") - for (include in includes) { - writer.writeLine("!includeurl $include") + includes.forEach { + writer.writeLine("!includeurl $it") } var viewTitle = view.title if (viewTitle.isNullOrBlank()) { @@ -67,19 +70,36 @@ internal class HeaderWriter(private val elementStyleWriter: ElementStyleWriter) writeAsyncRelTag(writer) } elementsStyles.forEach { style -> - elementStyleWriter.writeElementStyle(style, writer) + styleWriter.writeElementStyle(style, writer) + } + boundaryStyles.forEach { style -> + styleWriter.writeC4PumlStyle(style, writer, "Boundary") + } + personStyles.forEach { style -> + styleWriter.writeC4PumlStyle(style, writer, "Person") } } - private fun addSpriteIncludeUrls(view: ModelView) { - val appliedElementStyles = ElementStyleWriter.collectAppliedElementStyles(view) - val spriteIncludeUrls = appliedElementStyles + private fun addSpriteIncludeUrls( + elementStyles: List, + boundaryStyles: List, + ) { + val spriteIncludeUrls = elementStyles .asSequence() .map { listOf(it.sprite, it.legendSprite) } .flatten() .filterIsInstance() .map { it.includeUrl } .toMutableList() + spriteIncludeUrls.addAll( + boundaryStyles + .asSequence() + .map { listOf(it.sprite, it.legendSprite) } + .flatten() + .filterIsInstance() + .map { it.includeUrl } + .toMutableList() + ) if (spriteIncludeUrls.any { it.startsWith(AWS_ICON_URL) }) { spriteIncludeUrls.add(0, AWS_ICON_COMMONS) } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/StyleWriter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/StyleWriter.kt new file mode 100644 index 0000000..a84efed --- /dev/null +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/StyleWriter.kt @@ -0,0 +1,207 @@ +package com.github.chriskn.structurizrextension.internal.export.writer + +import com.github.chriskn.structurizrextension.api.view.style.C4Shape +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.ROUNDED_BOX +import com.github.chriskn.structurizrextension.api.view.style.getBoundaryStyles +import com.github.chriskn.structurizrextension.api.view.style.getElementStyles +import com.github.chriskn.structurizrextension.api.view.style.getPersonStyles +import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.Sprite +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.C4PumlStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.PersonStyle +import com.github.chriskn.structurizrextension.internal.export.view.getBoundaryContainer +import com.github.chriskn.structurizrextension.internal.export.view.getBoundarySystems +import com.structurizr.export.IndentingWriter +import com.structurizr.model.ModelItem +import com.structurizr.model.Person +import com.structurizr.view.Border +import com.structurizr.view.Border.Dashed +import com.structurizr.view.Border.Dotted +import com.structurizr.view.Border.Solid +import com.structurizr.view.ComponentView +import com.structurizr.view.ContainerView +import com.structurizr.view.ModelView + +@Suppress("TooManyFunctions") +internal object StyleWriter { + + fun collectAppliedElementStyles(view: ModelView): List { + val elements: MutableSet = view.elements.map { it.element }.toMutableSet() + val usedTags = elements.map { it.tagsAsSet }.flatten().toSet() + val stylesForTags = view.viewSet.getElementStyles().filter { usedTags.contains(it.tag) } + + view.getElementStyles().filter { usedTags.contains(it.tag) } + return stylesForTags.distinctBy { it.tag } + } + + fun collectAppliedBoundaryStyles(view: ModelView): List { + val elements: Set = when (view) { + is ContainerView -> view.getBoundarySystems().toSet() + is ComponentView -> view.getBoundaryContainer().toSet() + else -> emptySet() + } + val usedTags = elements.map { it.tagsAsSet }.flatten().toSet() + val stylesForTags = view.viewSet.getBoundaryStyles().filter { usedTags.contains(it.tag) } + + view.getBoundaryStyles().filter { usedTags.contains(it.tag) } + return stylesForTags.distinctBy { it.tag } + } + + fun collectAppliedPersonStyles(view: ModelView): List { + val person: MutableSet = view.elements.map { it.element }.filterIsInstance().toMutableSet() + val usedTags = person.map { it.tagsAsSet }.flatten().toSet() + val stylesForTags = view.viewSet.getPersonStyles().filter { usedTags.contains(it.tag) } + + view.getPersonStyles().filter { usedTags.contains(it.tag) } + return stylesForTags.distinctBy { it.tag } + } + + fun writeElementStyle(elementStyle: ElementStyle, writer: IndentingWriter) { + val bgColor = elementStyle.backgroundColor + val fontColor = elementStyle.fontColor + val borderColor = elementStyle.borderColor + val borderStyle = borderStyleString(elementStyle.border) + val borderThickness = elementStyle.borderWith?.toString() + val shadow = elementStyle.shadowing + val technology = elementStyle.technology + val shapeValue = elementStyle.c4Shape + val shape = shapeString(shapeValue) + val sprite = elementStyle.sprite?.toPlantUmlString() + val legendSprite = elementStyle.legendSprite?.toPlantUmlString() + val legendText = elementStyle.legendText + writer.writeLine( + """AddElementTag(${elementStyle.tag}${ + addIfNotNull("sprite", sprite) + }${ + addIfNotNull("bgColor", bgColor) + }${ + addIfNotNull("fontColor", fontColor) + }${ + addIfNotNull("borderColor", borderColor) + }${ + addIfNotNull("borderStyle", borderStyle) + }${ + addIfNotNull("borderThickness", borderThickness) + }${ + addIfNotNull("shadowing", shadow) + }${ + addIfNotNull("shape", shape) + }${ + addIfNotNull("techn", technology) + }${ + addIfNotNull("legendSprite", legendSprite) + }${ + addIfNotNull("legendText", legendText) + })""" + ) + } + + fun writeC4PumlStyle(c4PumlStyle: C4PumlStyle, writer: IndentingWriter, tagType: String) { + val bgColor = c4PumlStyle.backgroundColor + val fontColor = c4PumlStyle.fontColor + val borderColor = c4PumlStyle.borderColor + val borderStyle = borderStyleString(c4PumlStyle.border) + val borderThickness = c4PumlStyle.borderWith?.toString() + val shadow = c4PumlStyle.shadowing + val shapeValue = c4PumlStyle.c4Shape + val shape = shapeString(shapeValue) + val sprite = c4PumlStyle.sprite?.toPlantUmlString() + val legendSprite = c4PumlStyle.legendSprite?.toPlantUmlString() + val legendText = c4PumlStyle.legendText + writer.writeLine( + """Add${tagType}Tag(${c4PumlStyle.tag}${ + addIfNotNull("sprite", sprite) + }${ + addIfNotNull("bgColor", bgColor) + }${ + addIfNotNull("fontColor", fontColor) + }${ + addIfNotNull("borderColor", borderColor) + }${ + addIfNotNull("borderStyle", borderStyle) + }${ + addIfNotNull("borderThickness", borderThickness) + }${ + addIfNotNull("shadowing", shadow) + }${ + addIfNotNull("shape", shape) + }${ + addIfNotNull("legendSprite", legendSprite) + }${ + addIfNotNull("legendText", legendText) + })""" + ) + } + + private fun borderStyleString(border: Border?): String? { + val borderStyle = when (border) { + Solid -> "SolidLine()" + Dashed -> "DashedLine()" + Dotted -> "DottedLine()" + else -> null + } + return borderStyle + } + + private fun shapeString(shapeValue: C4Shape?): String? { + val shape = when (shapeValue) { + EIGHT_SIDED -> "EightSidedShape()" + ROUNDED_BOX -> "RoundedBoxShape()" + else -> null + } + return shape + } + + private fun addIfNotNull(name: String, value: Any?) = if (value != null) { + """, ${'$'}$name=$value""" + } else { + "" + } + + private fun Sprite.toPlantUmlString(): String = when (this) { + is PumlSprite -> """"${spriteString(this.name, scale, validatedColor)}"""" + is OpenIconicSprite -> """"&${spriteString(this.name, scale, validatedColor)}"""" + is ImageSprite -> { + val scaleString = scaleString(this.scale) + if (scaleString.isBlank()) { + """"$url"""" + } else { + """"$url{$scaleString}"""" + } + } + + else -> throw IllegalArgumentException("Unknown sprite type ${this::class}") + } + + private fun spriteString( + name: String, + scale: Double?, + color: String?, + ): String { + val scaleString = scaleString(scale) + val colorString = colorString(color) + return if (scaleString.isBlank() && colorString.isBlank()) { + name + } else if (colorString.isBlank()) { + "$name{$scaleString}" + } else if (scaleString.isBlank()) { + "$name{$colorString}" + } else { + "$name{$scaleString,$colorString}" + } + } + + private fun colorString(color: String?): String = if (color != null) { + "color=$color" + } else { + "" + } + + private fun scaleString(scale: Double?): String = if (scale != null) { + "scale=$scale" + } else { + "" + } +} diff --git a/src/main/kotlin/com/structurizr/view/ViewSetConfigurationPropertyExtions.kt b/src/main/kotlin/com/structurizr/view/ViewSetConfigurationPropertyExtions.kt new file mode 100644 index 0000000..bc0c3cb --- /dev/null +++ b/src/main/kotlin/com/structurizr/view/ViewSetConfigurationPropertyExtions.kt @@ -0,0 +1,23 @@ +package com.structurizr.view + +import com.github.chriskn.structurizrextension.api.view.style.BOUNDARY_STYLE_PROPERTY_NAME_PREFIX +import com.github.chriskn.structurizrextension.api.view.style.ELEMENT_STYLE_PROPERTY_NAME_PREFIX +import com.github.chriskn.structurizrextension.api.view.style.PERSON_STYLE_PROPERTY_NAME_PREFIX + +fun Configuration.clearBoundaryStyles() { + this.properties = this + .properties + .filterKeys { !it.startsWith(BOUNDARY_STYLE_PROPERTY_NAME_PREFIX) } +} + +fun Configuration.clearPersonStyles() { + this.properties = this + .properties + .filterKeys { !it.startsWith(PERSON_STYLE_PROPERTY_NAME_PREFIX) } +} + +fun Configuration.clearElementStyles() { + this.properties = this + .properties + .filterKeys { !it.startsWith(ELEMENT_STYLE_PROPERTY_NAME_PREFIX) } +} diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/ContextViewTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/ContextViewTest.kt index 28e487e..5be1865 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/ContextViewTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/ContextViewTest.kt @@ -63,7 +63,6 @@ class ContextViewTest { model.person( "Actor", link = "https://www.google.de", - tags = listOf("human"), uses = listOf( Dependency( system1, diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt index 8eef952..48aee77 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt @@ -4,12 +4,98 @@ import com.github.chriskn.structurizrextension.api.icons.IconRegistry import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows class SpriteTest { + @Nested + inner class Sprite { + + @Test + fun `PumlSprite is serialized and deserialized correctly`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + ) + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `PumlSprite is serialized and deserialized correctly when color is used`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + color = "green" + ) + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `PumlSprite is serialized and deserialized correctly when scale is used`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + scale = 0.4 + ) + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `PumlSprite is serialized and deserialized correctly when scale and color is used`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + scale = 0.4, + color = "green" + ) + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly`() { + val expectedSprite = OpenIconicSprite("folder") + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly with scale`() { + val expectedSprite = OpenIconicSprite("folder", scale = 0.4) + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly with color`() { + val expectedSprite = OpenIconicSprite("folder", color = "grey") + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly with scale and color`() { + val expectedSprite = OpenIconicSprite("folder", color = "grey", scale = 0.1) + val style = ElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + } + @Nested inner class ImageSpriteTest { diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleIntegrationTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/StyleIntegrationTest.kt similarity index 54% rename from src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleIntegrationTest.kt rename to src/test/kotlin/com/github/chriskn/structurizrextension/view/style/StyleIntegrationTest.kt index 59c5665..214c835 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleIntegrationTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/StyleIntegrationTest.kt @@ -1,56 +1,80 @@ package com.github.chriskn.structurizrextension.view.style import com.github.chriskn.structurizrextension.api.icons.IconRegistry +import com.github.chriskn.structurizrextension.api.model.Dependency import com.github.chriskn.structurizrextension.api.model.component import com.github.chriskn.structurizrextension.api.model.container +import com.github.chriskn.structurizrextension.api.model.person import com.github.chriskn.structurizrextension.api.model.softwareSystem +import com.github.chriskn.structurizrextension.api.view.componentView import com.github.chriskn.structurizrextension.api.view.containerView +import com.github.chriskn.structurizrextension.api.view.showExternalSoftwareSystemBoundaries import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED import com.github.chriskn.structurizrextension.api.view.style.C4Shape.ROUNDED_BOX +import com.github.chriskn.structurizrextension.api.view.style.addBoundaryStyle import com.github.chriskn.structurizrextension.api.view.style.addElementStyle +import com.github.chriskn.structurizrextension.api.view.style.addPersonStyle import com.github.chriskn.structurizrextension.api.view.style.clearElementStyles -import com.github.chriskn.structurizrextension.api.view.style.createElementStyle import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle +import com.github.chriskn.structurizrextension.api.view.style.styles.PersonStyle import com.github.chriskn.structurizrextension.api.view.systemContextView import com.github.chriskn.structurizrextension.assertExpectedDiagramWasWrittenForView import com.structurizr.Workspace import com.structurizr.view.Border.Dashed import com.structurizr.view.Border.Dotted +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -class ElementStyleIntegrationTest { +class StyleIntegrationTest { - private val pathToExpectedDiagrams = "view/style" + private val pathToExpectedDiagrams = "view/style/element" private val workspace = Workspace("My Workspace", "") private val model = workspace.model private val views = workspace.views private val systemTag = "System Style" + private val systemBoundaryTag = "System Boundary Style" private val containerTag = "Container Style" + private val containerBoundaryTag = "Container Boundary Style" private val componentTag = "Component Style" + private val personTag = "Person Style" - private val system = model.softwareSystem("My Software System", "system", tags = listOf(systemTag)) - private val container = system.container("My Container", "container", tags = listOf(containerTag)) + private val system = + model.softwareSystem("My Software System", "system", tags = listOf(systemTag, systemBoundaryTag)) + private val container = + system.container("My Container", "container", tags = listOf(containerTag, containerBoundaryTag)) private val component = container.component("My Component", "component", tags = listOf(componentTag)) + @BeforeAll + fun setUpModel() { + model.person( + name = "Person", + tags = listOf(personTag), + uses = listOf( + Dependency(system, "uses system"), + Dependency(container, "uses container"), + Dependency(component, "uses component"), + ) + ) + } + @BeforeEach fun resetStyles() { views.clearElementStyles() } @Test - fun `element style is applied for software systems`() { + fun `styles are applied to system context diagram`() { val diagramKey = "SystemStyleTest" - - workspace.views.systemContextView(system, diagramKey, "SystemStyleTest") - val sprite = ImageSprite("img:https://plantuml.com/logo3.png", 0.4) val legendSprite = OpenIconicSprite("compass", scale = 3.0) - val systemStyle = createElementStyle( + val systemStyle = ElementStyle( tag = systemTag, backgroundColor = "#000000", border = Dashed, @@ -64,18 +88,28 @@ class ElementStyleIntegrationTest { legendSprite = legendSprite, legendText = "this is a legend" ) + val personStyle = PersonStyle( + tag = personTag, + backgroundColor = "#00FF00", + border = Dashed, + borderWith = 4, + borderColor = "red", + fontColor = "blue", + c4Shape = EIGHT_SIDED, + legendText = "this is a legend" + ) views.addElementStyle(systemStyle) + views.addPersonStyle(personStyle) + + val view = workspace.views.systemContextView(system, diagramKey, "SystemStyleTest") + view.addAllPeople() assertExpectedDiagramWasWrittenForView(workspace, pathToExpectedDiagrams, diagramKey) } @Test - fun `element style is applied for container`() { + fun `element styles are applied to container`() { val diagramKey = "ContainerStyleTest" - - val containerView = workspace.views.containerView(system, diagramKey, "ContainerStyleTest") - containerView.addAllContainers() - val sprite = PumlSprite( includeUrl = IconRegistry.iconUrlFor("postgresql")!!, name = "postgresql", @@ -83,7 +117,7 @@ class ElementStyleIntegrationTest { color = "green" ) val legendSprite = OpenIconicSprite("compass") - val containerStyle = createElementStyle( + val containerStyle = ElementStyle( tag = containerTag, backgroundColor = "#ffffff", border = Dotted, @@ -95,9 +129,82 @@ class ElementStyleIntegrationTest { c4Shape = ROUNDED_BOX, sprite = sprite, legendSprite = legendSprite, - legendText = "this is a legend text" + legendText = "this is a legend container" + ) + val personStyle = PersonStyle( + tag = personTag, + backgroundColor = "#00FF00", + border = Dashed, + borderWith = 4, + borderColor = "red", + fontColor = "blue", + c4Shape = EIGHT_SIDED, + legendText = "this is a person" + ) + val boundaryStyle = BoundaryStyle( + tag = systemBoundaryTag, + backgroundColor = "#00FFFF", + border = Dotted, + borderWith = 4, + borderColor = "red", + fontColor = "green", + legendText = "this is a system" ) views.addElementStyle(containerStyle) + views.addPersonStyle(personStyle) + views.addBoundaryStyle(boundaryStyle) + + val containerView = workspace.views.containerView(system, diagramKey, "ContainerStyleTest") + containerView.addAllContainers() + containerView.addAllPeople() + containerView.showExternalSoftwareSystemBoundaries = true + + assertExpectedDiagramWasWrittenForView(workspace, pathToExpectedDiagrams, diagramKey) + } + + @Test + fun `element styles are applied to component`() { + val diagramKey = "ComponentStyleTest" + val sprite = OpenIconicSprite("compass") + val componentStyle = ElementStyle( + tag = componentTag, + backgroundColor = "#ffffff", + border = Dotted, + borderWith = 5, + borderColor = "purple", + fontColor = "red", + shadowing = false, + technology = "REST", + c4Shape = ROUNDED_BOX, + sprite = sprite, + legendSprite = sprite, + legendText = "this is a legend text" + ) + val boundaryStyle = BoundaryStyle( + tag = systemBoundaryTag, + backgroundColor = "#00FFFF", + border = Dotted, + borderWith = 4, + borderColor = "red", + fontColor = "green", + legendText = "this is a system" + ) + val personStyle = PersonStyle( + tag = personTag, + backgroundColor = "#00FF00", + border = Dashed, + borderWith = 4, + borderColor = "red", + fontColor = "blue", + c4Shape = EIGHT_SIDED, + legendText = "this is a person" + ) + views.addElementStyle(componentStyle) + views.addPersonStyle(personStyle) + views.addBoundaryStyle(boundaryStyle) + + val componentView = workspace.views.componentView(container, diagramKey, "ComponentStyleTest") + componentView.addAllComponents() assertExpectedDiagramWasWrittenForView(workspace, pathToExpectedDiagrams, diagramKey) } @@ -106,7 +213,6 @@ class ElementStyleIntegrationTest { fun `element style can be applied for single views`() { val diagramKeyWithStyle = "ViewStyleTestWithStyle" val diagramKeyWithoutStyle = "ViewStyleTestWithoutStyle" - val sprite = PumlSprite( includeUrl = IconRegistry.iconUrlFor("postgresql")!!, name = "postgresql", @@ -114,7 +220,7 @@ class ElementStyleIntegrationTest { color = "green" ) val legendSprite = OpenIconicSprite("compass") - val containerTag = createElementStyle( + val containerTag = ElementStyle( tag = containerTag, backgroundColor = "#ffffff", border = Dotted, @@ -128,6 +234,7 @@ class ElementStyleIntegrationTest { legendSprite = legendSprite, legendText = "this is a legend text" ) + val containerViewWithStyle = workspace.views.containerView(system, diagramKeyWithStyle, "ViewStyleTestWithStyle") containerViewWithStyle.addAllContainers() @@ -144,7 +251,7 @@ class ElementStyleIntegrationTest { fun `element style for unused tags are not exported`() { val diagramKey = "ViewStyleTestUnusedTag" - val unusedStyle = createElementStyle( + val unusedStyle = ElementStyle( tag = "someUnusedTag", backgroundColor = "#ffffff", legendText = "this is a legend text" diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/boundary/BoundaryStyleExtensionTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/boundary/BoundaryStyleExtensionTest.kt new file mode 100644 index 0000000..240fee6 --- /dev/null +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/boundary/BoundaryStyleExtensionTest.kt @@ -0,0 +1,193 @@ +package com.github.chriskn.structurizrextension.view.style.boundary + +import com.github.chriskn.structurizrextension.api.icons.IconRegistry +import com.github.chriskn.structurizrextension.api.model.softwareSystem +import com.github.chriskn.structurizrextension.api.view.containerView +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.ROUNDED_BOX +import com.github.chriskn.structurizrextension.api.view.style.addBoundaryStyle +import com.github.chriskn.structurizrextension.api.view.style.getBoundaryStyles +import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.internal.export.view.style.toJson +import com.structurizr.Workspace +import com.structurizr.view.Border.Dashed +import com.structurizr.view.Border.Dotted +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class BoundaryStyleExtensionTest { + + @Test + fun `boundary style values are correctly set`() { + val expSprite = ImageSprite("img:https://plantuml.com/logo3.png", 0.4) + val expLegendSprite = OpenIconicSprite("compass", scale = 3.0, color = "blue") + val expTag = "styleTag" + val expBackgroundColor = "#000000" + val expBorder = Dashed + val expBorderWith = 4 + val expBorderColor = "#008000" + val expFontColor = "#ffffff" + val expShadowing = true + val expC4Shape = EIGHT_SIDED + val expLegendText = "this is a legend" + + val style = BoundaryStyle( + tag = expTag, + backgroundColor = expBackgroundColor, + border = expBorder, + borderWith = expBorderWith, + borderColor = expBorderColor, + fontColor = expFontColor, + shadowing = expShadowing, + c4Shape = expC4Shape, + sprite = expSprite, + legendSprite = expLegendSprite, + legendText = expLegendText + ) + + assertThat(style.tag).isEqualTo(expTag) + assertThat(style.backgroundColor).isEqualTo(expBackgroundColor) + assertThat(style.border).isEqualTo(expBorder) + assertThat(style.borderWith).isEqualTo(expBorderWith) + assertThat(style.borderColor).isEqualTo(expBorderColor) + assertThat(style.fontColor).isEqualTo(expFontColor) + assertThat(style.shadowing).isEqualTo(expShadowing) + assertThat(style.c4Shape).isEqualTo(expC4Shape) + assertThat(style.sprite).isEqualTo(expSprite) + assertThat(style.legendSprite).isEqualTo(expLegendSprite) + assertThat(style.legendText).isEqualTo(expLegendText) + } + + @Test + fun `boundary style can be added to ViewSet`() { + val sprite = PumlSprite( + includeUrl = IconRegistry.iconUrlFor("postgresql")!!, + name = "postgresql", + scale = 0.5, + color = "green" + ) + val legendSprite = OpenIconicSprite("compass") + val style1 = BoundaryStyle( + tag = "tag", + backgroundColor = "#ffffff", + border = Dotted, + borderWith = 5, + borderColor = "purple", + fontColor = "red", + shadowing = false, + c4Shape = ROUNDED_BOX, + sprite = sprite, + legendSprite = legendSprite, + legendText = "this is a legend text" + ) + val style2 = BoundaryStyle("tag1") + + val workspace = Workspace("test", "test") + val views = workspace.views + + views.addBoundaryStyle(style1) + views.addBoundaryStyle(style2) + + val personStyles = views.getBoundaryStyles() + assertThat(personStyles).hasSize(2) + assertThat(personStyles.firstOrNull { it.tag == style1.tag }).isEqualTo(style1) + assertThat(personStyles.firstOrNull { it.tag == style2.tag }).isEqualTo(style2) + } + + @Test + fun `boundary style can be added to View`() { + val style1 = BoundaryStyle( + tag = "tag", + backgroundColor = "#ffffff", + border = Dotted, + borderWith = 5, + ) + val style2 = BoundaryStyle("tag1") + + val workspace = Workspace("test", "test") + val views = workspace.views + + val system = workspace.model.softwareSystem("test", "test") + val view = views.containerView(system, "testview", "desc") + view.addBoundaryStyle(style1) + view.addBoundaryStyle(style2) + + val styles = view.getBoundaryStyles() + assertThat(styles).hasSize(2) + assertThat(styles.map { it.toJson() }).contains(style1.toJson(), style2.toJson()) + } + + @Test + fun `IllegalArgumentException is thrown when tag is blank`() { + assertThrows { + BoundaryStyle(" ") + } + } + + @Test + fun `boundary style can be initialized with null values`() { + val expTag = "tag" + + val style = BoundaryStyle(tag = expTag) + + assertThat(style.tag).isEqualTo(expTag) + assertThat(style.backgroundColor).isNull() + assertThat(style.border).isNull() + assertThat(style.borderWith).isNull() + assertThat(style.borderColor).isNull() + assertThat(style.fontColor).isNull() + assertThat(style.shadowing).isFalse() + assertThat(style.c4Shape).isNull() + assertThat(style.sprite).isNull() + assertThat(style.legendSprite).isNull() + assertThat(style.legendText).isNull() + } + + @Nested + inner class Color { + + @Test + fun `IllegalArgumentException is thrown for invalid background color`() { + assertThrows { + BoundaryStyle("test", backgroundColor = "ABC") + } + } + + @Test + fun `IllegalArgumentException is thrown for invalid font color`() { + assertThrows { + BoundaryStyle("test", fontColor = "jellow") + } + } + + @Test + fun `IllegalArgumentException is thrown for invalid border color`() { + assertThrows { + BoundaryStyle("test", borderColor = "") + } + } + + @Test + fun `named border color is translated to hex color`() { + val elementStyle = BoundaryStyle("test", borderColor = "green") + assertThat(elementStyle.borderColor).isEqualTo("#008000") + } + + @Test + fun `named font color is translated to hex color`() { + val elementStyle = BoundaryStyle("test", fontColor = "black") + assertThat(elementStyle.fontColor).isEqualTo("#000000") + } + + @Test + fun `named background color is translated to hex color`() { + val elementStyle = BoundaryStyle("test", backgroundColor = "white") + assertThat(elementStyle.backgroundColor).isEqualTo("#ffffff") + } + } +} diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/boundary/BoundaryStyleJsonTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/boundary/BoundaryStyleJsonTest.kt new file mode 100644 index 0000000..b105cfa --- /dev/null +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/boundary/BoundaryStyleJsonTest.kt @@ -0,0 +1,58 @@ +package com.github.chriskn.structurizrextension.view.style.boundary + +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED +import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite +import com.github.chriskn.structurizrextension.api.view.style.styles.BoundaryStyle +import com.github.chriskn.structurizrextension.internal.export.view.style.elementStyleFromJson +import com.github.chriskn.structurizrextension.internal.export.view.style.toJson +import com.structurizr.view.Border.Dashed +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class BoundaryStyleJsonTest { + + @Test + fun `person is serialized and deserialized correctly`() { + val expSprite = ImageSprite("img:https://plantuml.com/logo3.png", 0.4) + val expLegendSprite = OpenIconicSprite("compass", scale = 3.0, color = "blue") + val expTag = "styleTag" + val expBackgroundColor = "#000000" + val expBorder = Dashed + val expBorderWith = 4 + val expBorderColor = "#008000" + val expFontColor = "#ffffff" + val expShadowing = true + val expC4Shape = EIGHT_SIDED + val expLegendText = "this is a legend" + + val style = BoundaryStyle( + tag = expTag, + backgroundColor = expBackgroundColor, + border = expBorder, + borderWith = expBorderWith, + borderColor = expBorderColor, + fontColor = expFontColor, + shadowing = expShadowing, + c4Shape = expC4Shape, + sprite = expSprite, + legendSprite = expLegendSprite, + legendText = expLegendText + ) + + val styleJson = style.toJson() + val deserializedStyle = elementStyleFromJson(styleJson) + + assertThat(deserializedStyle.tag).isEqualTo(expTag) + assertThat(deserializedStyle.backgroundColor).isEqualTo(expBackgroundColor) + assertThat(deserializedStyle.border).isEqualTo(expBorder) + assertThat(deserializedStyle.borderWith).isEqualTo(expBorderWith) + assertThat(deserializedStyle.borderColor).isEqualTo(expBorderColor) + assertThat(deserializedStyle.fontColor).isEqualTo(expFontColor) + assertThat(deserializedStyle.shadowing).isEqualTo(expShadowing) + assertThat(deserializedStyle.c4Shape).isEqualTo(expC4Shape) + assertThat(deserializedStyle.sprite).isEqualTo(expSprite) + assertThat(deserializedStyle.legendSprite).isEqualTo(expLegendSprite) + assertThat(deserializedStyle.legendText).isEqualTo(expLegendText) + } +} diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleExtensionTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/element/ElementStyleExtensionTest.kt similarity index 53% rename from src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleExtensionTest.kt rename to src/test/kotlin/com/github/chriskn/structurizrextension/view/style/element/ElementStyleExtensionTest.kt index a87b5a1..affd922 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleExtensionTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/element/ElementStyleExtensionTest.kt @@ -1,4 +1,4 @@ -package com.github.chriskn.structurizrextension.view.style +package com.github.chriskn.structurizrextension.view.style.element import com.github.chriskn.structurizrextension.api.icons.IconRegistry import com.github.chriskn.structurizrextension.api.model.softwareSystem @@ -6,21 +6,11 @@ import com.github.chriskn.structurizrextension.api.view.containerView import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED import com.github.chriskn.structurizrextension.api.view.style.C4Shape.ROUNDED_BOX import com.github.chriskn.structurizrextension.api.view.style.addElementStyle -import com.github.chriskn.structurizrextension.api.view.style.backgroundColor -import com.github.chriskn.structurizrextension.api.view.style.borderColor -import com.github.chriskn.structurizrextension.api.view.style.borderWith -import com.github.chriskn.structurizrextension.api.view.style.c4Shape -import com.github.chriskn.structurizrextension.api.view.style.createElementStyle -import com.github.chriskn.structurizrextension.api.view.style.fontColor import com.github.chriskn.structurizrextension.api.view.style.getElementStyles -import com.github.chriskn.structurizrextension.api.view.style.legendSprite -import com.github.chriskn.structurizrextension.api.view.style.legendText -import com.github.chriskn.structurizrextension.api.view.style.shadowing -import com.github.chriskn.structurizrextension.api.view.style.sprite import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite -import com.github.chriskn.structurizrextension.api.view.style.technology +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle import com.github.chriskn.structurizrextension.internal.export.view.style.toJson import com.structurizr.Workspace import com.structurizr.view.Border.Dashed @@ -47,7 +37,7 @@ class ElementStyleExtensionTest { val expC4Shape = EIGHT_SIDED val expLegendText = "this is a legend" - val style = createElementStyle( + val style = ElementStyle( tag = expTag, backgroundColor = expBackgroundColor, border = expBorder, @@ -85,7 +75,7 @@ class ElementStyleExtensionTest { color = "green" ) val legendSprite = OpenIconicSprite("compass") - val style1 = createElementStyle( + val style1 = ElementStyle( tag = "tag", backgroundColor = "#ffffff", border = Dotted, @@ -99,7 +89,7 @@ class ElementStyleExtensionTest { legendSprite = legendSprite, legendText = "this is a legend text" ) - val style2 = createElementStyle("tag1") + val style2 = ElementStyle("tag1") val workspace = Workspace("test", "test") val views = workspace.views @@ -108,19 +98,19 @@ class ElementStyleExtensionTest { views.addElementStyle(style2) assertThat(views.getElementStyles()).hasSize(2) - assertThat(views.getElementStyles()[0]).isEqualTo(style1) - assertThat(views.getElementStyles()[1]).isEqualTo(style2) + assertThat(views.getElementStyles().firstOrNull { it.tag == style1.tag }).isEqualTo(style1) + assertThat(views.getElementStyles().firstOrNull { it.tag == style2.tag }).isEqualTo(style2) } @Test fun `element style can be added to View`() { - val style1 = createElementStyle( + val style1 = ElementStyle( tag = "tag", backgroundColor = "#ffffff", border = Dotted, borderWith = 5, ) - val style2 = createElementStyle("tag1") + val style2 = ElementStyle("tag1") val workspace = Workspace("test", "test") val views = workspace.views @@ -138,7 +128,7 @@ class ElementStyleExtensionTest { @Test fun `IllegalArgumentException is thrown when tag is blank`() { assertThrows { - createElementStyle(" ") + ElementStyle(" ") } } @@ -146,7 +136,7 @@ class ElementStyleExtensionTest { fun `element style can be initialized with null values`() { val expTag = "tag" - val style = createElementStyle(tag = expTag) + val style = ElementStyle(tag = expTag) assertThat(style.tag).isEqualTo(expTag) assertThat(style.backgroundColor).isNull() @@ -168,140 +158,40 @@ class ElementStyleExtensionTest { @Test fun `IllegalArgumentException is thrown for invalid background color`() { assertThrows { - createElementStyle("test", backgroundColor = "ABC") + ElementStyle("test", backgroundColor = "ABC") } } @Test fun `IllegalArgumentException is thrown for invalid font color`() { assertThrows { - createElementStyle("test", fontColor = "jellow") + ElementStyle("test", fontColor = "jellow") } } @Test fun `IllegalArgumentException is thrown for invalid border color`() { assertThrows { - createElementStyle("test", borderColor = "") + ElementStyle("test", borderColor = "") } } @Test fun `named border color is translated to hex color`() { - val elementStyle = createElementStyle("test", borderColor = "green") + val elementStyle = ElementStyle("test", borderColor = "green") assertThat(elementStyle.borderColor).isEqualTo("#008000") } @Test fun `named font color is translated to hex color`() { - val elementStyle = createElementStyle("test", fontColor = "black") + val elementStyle = ElementStyle("test", fontColor = "black") assertThat(elementStyle.fontColor).isEqualTo("#000000") } @Test fun `named background color is translated to hex color`() { - val elementStyle = createElementStyle("test", backgroundColor = "white") + val elementStyle = ElementStyle("test", backgroundColor = "white") assertThat(elementStyle.backgroundColor).isEqualTo("#ffffff") } } - - @Nested - inner class Sprite { - - @Test - fun `PumlSprite is serialized and deserialized correctly`() { - val expectedSprite = PumlSprite( - name = "android", - includeUrl = "https://test.com/sprites/android-icon.puml", - ) - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `PumlSprite is serialized and deserialized correctly when color is used`() { - val expectedSprite = PumlSprite( - name = "android", - includeUrl = "https://test.com/sprites/android-icon.puml", - color = "green" - ) - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `PumlSprite is serialized and deserialized correctly when scale is used`() { - val expectedSprite = PumlSprite( - name = "android", - includeUrl = "https://test.com/sprites/android-icon.puml", - scale = 0.4 - ) - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `PumlSprite is serialized and deserialized correctly when scale and color is used`() { - val expectedSprite = PumlSprite( - name = "android", - includeUrl = "https://test.com/sprites/android-icon.puml", - scale = 0.4, - color = "green" - ) - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `OpenIconicSprite is serialized and deserialized correctly`() { - val expectedSprite = OpenIconicSprite("folder") - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `OpenIconicSprite is serialized and deserialized correctly with scale`() { - val expectedSprite = OpenIconicSprite("folder", scale = 0.4) - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `OpenIconicSprite is serialized and deserialized correctly with color`() { - val expectedSprite = OpenIconicSprite("folder", color = "grey") - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `OpenIconicSprite is serialized and deserialized correctly with scale and color`() { - val expectedSprite = OpenIconicSprite("folder", color = "grey", scale = 0.1) - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `ImageSprite is serialized and deserialized correctly when URI with scale is used`() { - val expectedSprite = ImageSprite("img:https://plantuml.com/logo.png", 0.4) - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - - @Test - fun `ImageSprite is serialized and deserialized correctly when URI without scale is used`() { - val expectedSprite = ImageSprite("img:https://plantuml.com/logo.png") - val style = createElementStyle("test", sprite = expectedSprite) - - assertThat(style.sprite).isEqualTo(expectedSprite) - } - } } diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleJsonTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/element/ElementStyleJsonTest.kt similarity index 72% rename from src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleJsonTest.kt rename to src/test/kotlin/com/github/chriskn/structurizrextension/view/style/element/ElementStyleJsonTest.kt index 648e730..375563d 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleJsonTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/element/ElementStyleJsonTest.kt @@ -1,19 +1,9 @@ -package com.github.chriskn.structurizrextension.view.style +package com.github.chriskn.structurizrextension.view.style.element import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED -import com.github.chriskn.structurizrextension.api.view.style.backgroundColor -import com.github.chriskn.structurizrextension.api.view.style.borderColor -import com.github.chriskn.structurizrextension.api.view.style.borderWith -import com.github.chriskn.structurizrextension.api.view.style.c4Shape -import com.github.chriskn.structurizrextension.api.view.style.createElementStyle -import com.github.chriskn.structurizrextension.api.view.style.fontColor -import com.github.chriskn.structurizrextension.api.view.style.legendSprite -import com.github.chriskn.structurizrextension.api.view.style.legendText -import com.github.chriskn.structurizrextension.api.view.style.shadowing -import com.github.chriskn.structurizrextension.api.view.style.sprite import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite -import com.github.chriskn.structurizrextension.api.view.style.technology +import com.github.chriskn.structurizrextension.api.view.style.styles.ElementStyle import com.github.chriskn.structurizrextension.internal.export.view.style.elementStyleFromJson import com.github.chriskn.structurizrextension.internal.export.view.style.toJson import com.structurizr.view.Border.Dashed @@ -23,7 +13,7 @@ import org.junit.jupiter.api.Test class ElementStyleJsonTest { @Test - fun `ElementStyle is serialized and deserialized correctly`() { + fun `element style is serialized and deserialized correctly`() { val expSprite = ImageSprite("img:https://plantuml.com/logo3.png", 0.4) val expLegendSprite = OpenIconicSprite("compass", scale = 3.0, color = "blue") val expTag = "styleTag" @@ -37,7 +27,7 @@ class ElementStyleJsonTest { val expC4Shape = EIGHT_SIDED val expLegendText = "this is a legend" - val style = createElementStyle( + val style = ElementStyle( tag = expTag, backgroundColor = expBackgroundColor, border = expBorder, diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/person/PersonStyleExtensionTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/person/PersonStyleExtensionTest.kt new file mode 100644 index 0000000..ea4b009 --- /dev/null +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/person/PersonStyleExtensionTest.kt @@ -0,0 +1,193 @@ +package com.github.chriskn.structurizrextension.view.style.person + +import com.github.chriskn.structurizrextension.api.icons.IconRegistry +import com.github.chriskn.structurizrextension.api.model.softwareSystem +import com.github.chriskn.structurizrextension.api.view.containerView +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.ROUNDED_BOX +import com.github.chriskn.structurizrextension.api.view.style.addPersonStyle +import com.github.chriskn.structurizrextension.api.view.style.getPersonStyles +import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite +import com.github.chriskn.structurizrextension.api.view.style.styles.PersonStyle +import com.github.chriskn.structurizrextension.internal.export.view.style.toJson +import com.structurizr.Workspace +import com.structurizr.view.Border.Dashed +import com.structurizr.view.Border.Dotted +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class PersonStyleExtensionTest { + + @Test + fun `person style values are correctly set`() { + val expSprite = ImageSprite("img:https://plantuml.com/logo3.png", 0.4) + val expLegendSprite = OpenIconicSprite("compass", scale = 3.0, color = "blue") + val expTag = "styleTag" + val expBackgroundColor = "#000000" + val expBorder = Dashed + val expBorderWith = 4 + val expBorderColor = "#008000" + val expFontColor = "#ffffff" + val expShadowing = true + val expC4Shape = EIGHT_SIDED + val expLegendText = "this is a legend" + + val style = PersonStyle( + tag = expTag, + backgroundColor = expBackgroundColor, + border = expBorder, + borderWith = expBorderWith, + borderColor = expBorderColor, + fontColor = expFontColor, + shadowing = expShadowing, + c4Shape = expC4Shape, + sprite = expSprite, + legendSprite = expLegendSprite, + legendText = expLegendText + ) + + assertThat(style.tag).isEqualTo(expTag) + assertThat(style.backgroundColor).isEqualTo(expBackgroundColor) + assertThat(style.border).isEqualTo(expBorder) + assertThat(style.borderWith).isEqualTo(expBorderWith) + assertThat(style.borderColor).isEqualTo(expBorderColor) + assertThat(style.fontColor).isEqualTo(expFontColor) + assertThat(style.shadowing).isEqualTo(expShadowing) + assertThat(style.c4Shape).isEqualTo(expC4Shape) + assertThat(style.sprite).isEqualTo(expSprite) + assertThat(style.legendSprite).isEqualTo(expLegendSprite) + assertThat(style.legendText).isEqualTo(expLegendText) + } + + @Test + fun `person style can be added to ViewSet`() { + val sprite = PumlSprite( + includeUrl = IconRegistry.iconUrlFor("postgresql")!!, + name = "postgresql", + scale = 0.5, + color = "green" + ) + val legendSprite = OpenIconicSprite("compass") + val style1 = PersonStyle( + tag = "tag", + backgroundColor = "#ffffff", + border = Dotted, + borderWith = 5, + borderColor = "purple", + fontColor = "red", + shadowing = false, + c4Shape = ROUNDED_BOX, + sprite = sprite, + legendSprite = legendSprite, + legendText = "this is a legend text" + ) + val style2 = PersonStyle("tag1") + + val workspace = Workspace("test", "test") + val views = workspace.views + + views.addPersonStyle(style1) + views.addPersonStyle(style2) + + val personStyles = views.getPersonStyles() + assertThat(personStyles).hasSize(2) + assertThat(personStyles.firstOrNull { it.tag == style1.tag }).isEqualTo(style1) + assertThat(personStyles.firstOrNull { it.tag == style2.tag }).isEqualTo(style2) + } + + @Test + fun `person style can be added to View`() { + val style1 = PersonStyle( + tag = "tag", + backgroundColor = "#ffffff", + border = Dotted, + borderWith = 5, + ) + val style2 = PersonStyle("tag1") + + val workspace = Workspace("test", "test") + val views = workspace.views + + val system = workspace.model.softwareSystem("test", "test") + val view = views.containerView(system, "testview", "desc") + view.addPersonStyle(style1) + view.addPersonStyle(style2) + + val styles = view.getPersonStyles() + assertThat(styles).hasSize(2) + assertThat(styles.map { it.toJson() }).contains(style1.toJson(), style2.toJson()) + } + + @Test + fun `IllegalArgumentException is thrown when tag is blank`() { + assertThrows { + PersonStyle(" ") + } + } + + @Test + fun `person style can be initialized with null values`() { + val expTag = "tag" + + val style = PersonStyle(tag = expTag) + + assertThat(style.tag).isEqualTo(expTag) + assertThat(style.backgroundColor).isNull() + assertThat(style.border).isNull() + assertThat(style.borderWith).isNull() + assertThat(style.borderColor).isNull() + assertThat(style.fontColor).isNull() + assertThat(style.shadowing).isFalse() + assertThat(style.c4Shape).isNull() + assertThat(style.sprite).isNull() + assertThat(style.legendSprite).isNull() + assertThat(style.legendText).isNull() + } + + @Nested + inner class Color { + + @Test + fun `IllegalArgumentException is thrown for invalid background color`() { + assertThrows { + PersonStyle("test", backgroundColor = "ABC") + } + } + + @Test + fun `IllegalArgumentException is thrown for invalid font color`() { + assertThrows { + PersonStyle("test", fontColor = "jellow") + } + } + + @Test + fun `IllegalArgumentException is thrown for invalid border color`() { + assertThrows { + PersonStyle("test", borderColor = "") + } + } + + @Test + fun `named border color is translated to hex color`() { + val elementStyle = PersonStyle("test", borderColor = "green") + assertThat(elementStyle.borderColor).isEqualTo("#008000") + } + + @Test + fun `named font color is translated to hex color`() { + val elementStyle = PersonStyle("test", fontColor = "black") + assertThat(elementStyle.fontColor).isEqualTo("#000000") + } + + @Test + fun `named background color is translated to hex color`() { + val elementStyle = PersonStyle("test", backgroundColor = "white") + assertThat(elementStyle.backgroundColor).isEqualTo("#ffffff") + } + } +} diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/person/PersonStyleJsonTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/person/PersonStyleJsonTest.kt new file mode 100644 index 0000000..20bad6a --- /dev/null +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/person/PersonStyleJsonTest.kt @@ -0,0 +1,58 @@ +package com.github.chriskn.structurizrextension.view.style.person + +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED +import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite +import com.github.chriskn.structurizrextension.api.view.style.styles.PersonStyle +import com.github.chriskn.structurizrextension.internal.export.view.style.elementStyleFromJson +import com.github.chriskn.structurizrextension.internal.export.view.style.toJson +import com.structurizr.view.Border.Dashed +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class PersonStyleJsonTest { + + @Test + fun `person is serialized and deserialized correctly`() { + val expSprite = ImageSprite("img:https://plantuml.com/logo3.png", 0.4) + val expLegendSprite = OpenIconicSprite("compass", scale = 3.0, color = "blue") + val expTag = "styleTag" + val expBackgroundColor = "#000000" + val expBorder = Dashed + val expBorderWith = 4 + val expBorderColor = "#008000" + val expFontColor = "#ffffff" + val expShadowing = true + val expC4Shape = EIGHT_SIDED + val expLegendText = "this is a legend" + + val style = PersonStyle( + tag = expTag, + backgroundColor = expBackgroundColor, + border = expBorder, + borderWith = expBorderWith, + borderColor = expBorderColor, + fontColor = expFontColor, + shadowing = expShadowing, + c4Shape = expC4Shape, + sprite = expSprite, + legendSprite = expLegendSprite, + legendText = expLegendText + ) + + val styleJson = style.toJson() + val deserializedStyle = elementStyleFromJson(styleJson) + + assertThat(deserializedStyle.tag).isEqualTo(expTag) + assertThat(deserializedStyle.backgroundColor).isEqualTo(expBackgroundColor) + assertThat(deserializedStyle.border).isEqualTo(expBorder) + assertThat(deserializedStyle.borderWith).isEqualTo(expBorderWith) + assertThat(deserializedStyle.borderColor).isEqualTo(expBorderColor) + assertThat(deserializedStyle.fontColor).isEqualTo(expFontColor) + assertThat(deserializedStyle.shadowing).isEqualTo(expShadowing) + assertThat(deserializedStyle.c4Shape).isEqualTo(expC4Shape) + assertThat(deserializedStyle.sprite).isEqualTo(expSprite) + assertThat(deserializedStyle.legendSprite).isEqualTo(expLegendSprite) + assertThat(deserializedStyle.legendText).isEqualTo(expLegendText) + } +} diff --git a/src/test/resources/expected/view/component/ComponentWithBoundary.puml b/src/test/resources/expected/view/component/ComponentWithBoundary.puml index de97ddc..1558a1b 100644 --- a/src/test/resources/expected/view/component/ComponentWithBoundary.puml +++ b/src/test/resources/expected/view/component/ComponentWithBoundary.puml @@ -14,7 +14,7 @@ Container_Boundary("MySoftwareSystem.NewBackendApp_boundary", "New Backend App") ComponentDb(MySoftwareSystem.NewBackendApp.Cache, "Cache", "RocksDB", "In Memory DB", "rocksdb", $link="https://google.de") WithoutPropertyHeader() AddProperty("jdbcUrl", "someurl") - Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "") + Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "", $tags="repo+persistence" ) Component(MySoftwareSystem.NewBackendApp.MyRestController, "MyRestController", "REST", "Provides data via rest", "") Component(MySoftwareSystem.NewBackendApp.MyService, "MyService", "", "Does implement some logic", "kotlin", $link="https://google.de") } diff --git a/src/test/resources/expected/view/component/ComponentWithContainers.puml b/src/test/resources/expected/view/component/ComponentWithContainers.puml index 31ba0fa..299b0af 100644 --- a/src/test/resources/expected/view/component/ComponentWithContainers.puml +++ b/src/test/resources/expected/view/component/ComponentWithContainers.puml @@ -16,7 +16,7 @@ Container(MySoftwareSystem.FrontendApp, "Frontend App", "TS", "some frontend", " ComponentDb(MySoftwareSystem.NewBackendApp.Cache, "Cache", "RocksDB", "In Memory DB", "rocksdb", $link="https://google.de") WithoutPropertyHeader() AddProperty("jdbcUrl", "someurl") -Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "") +Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "", $tags="repo+persistence" ) Component(MySoftwareSystem.NewBackendApp.MyRestController, "MyRestController", "REST", "Provides data via rest", "") Component(MySoftwareSystem.NewBackendApp.MyService, "MyService", "", "Does implement some logic", "kotlin", $link="https://google.de") Rel(MySoftwareSystem.NewBackendApp.MyRepo, MySoftwareSystem.Database, "gets data from") diff --git a/src/test/resources/expected/view/component/ComponentWithContainersAndBoundaries.puml b/src/test/resources/expected/view/component/ComponentWithContainersAndBoundaries.puml index 994bc69..d24e890 100644 --- a/src/test/resources/expected/view/component/ComponentWithContainersAndBoundaries.puml +++ b/src/test/resources/expected/view/component/ComponentWithContainersAndBoundaries.puml @@ -18,7 +18,7 @@ Container_Boundary("MySoftwareSystem.NewBackendApp_boundary", "New Backend App") ComponentDb(MySoftwareSystem.NewBackendApp.Cache, "Cache", "RocksDB", "In Memory DB", "rocksdb", $link="https://google.de") WithoutPropertyHeader() AddProperty("jdbcUrl", "someurl") - Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "") + Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "", $tags="repo+persistence" ) Component(MySoftwareSystem.NewBackendApp.MyRestController, "MyRestController", "REST", "Provides data via rest", "") Component(MySoftwareSystem.NewBackendApp.MyService, "MyService", "", "Does implement some logic", "kotlin", $link="https://google.de") } diff --git a/src/test/resources/expected/view/component/ComponentWithoutBoundary.puml b/src/test/resources/expected/view/component/ComponentWithoutBoundary.puml index 0a9419e..369104e 100644 --- a/src/test/resources/expected/view/component/ComponentWithoutBoundary.puml +++ b/src/test/resources/expected/view/component/ComponentWithoutBoundary.puml @@ -13,7 +13,7 @@ AddRelTag("async relationship", $textColor="$ARROW_COLOR", $lineColor="$ARROW_CO ComponentDb(MySoftwareSystem.NewBackendApp.Cache, "Cache", "RocksDB", "In Memory DB", "rocksdb", $link="https://google.de") WithoutPropertyHeader() AddProperty("jdbcUrl", "someurl") -Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "") +Component(MySoftwareSystem.NewBackendApp.MyRepo, "MyRepo", "Kotlin, JDBC", "Provides CRUD operations for data", "", $tags="repo+persistence" ) Component(MySoftwareSystem.NewBackendApp.MyRestController, "MyRestController", "REST", "Provides data via rest", "") Component(MySoftwareSystem.NewBackendApp.MyService, "MyService", "", "Does implement some logic", "kotlin", $link="https://google.de") Rel(MySoftwareSystem.NewBackendApp.MyRestController, MySoftwareSystem.NewBackendApp.MyService, "calls") diff --git a/src/test/resources/expected/view/style/ContainerStyleTest.puml b/src/test/resources/expected/view/style/ContainerStyleTest.puml deleted file mode 100644 index 8e39df3..0000000 --- a/src/test/resources/expected/view/style/ContainerStyleTest.puml +++ /dev/null @@ -1,16 +0,0 @@ -@startuml(id=ContainerStyleTest) -!includeurl https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml -!includeurl https://raw.githubusercontent.com/plantuml-stdlib/gilbarbara-plantuml-sprites/master/sprites/postgresql.puml -title My Software System - Containers -caption ContainerStyleTest - -SHOW_PERSON_OUTLINE() -LAYOUT_TOP_DOWN() - -AddElementTag(Container Style, $sprite="postgresql{scale=0.5,color=#008000}", $bgColor=#ffffff, $fontColor=#ff0000, $borderColor=#800080, $borderStyle=DottedLine(), $borderThickness=5, $shadowing=false, $shape=RoundedBoxShape(), $techn=REST, $legendSprite="&compass", $legendText=this is a legend text) -Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style" ) - - -SHOW_LEGEND(true) - -@enduml \ No newline at end of file diff --git a/src/test/resources/expected/view/style/element/ComponentStyleTest.puml b/src/test/resources/expected/view/style/element/ComponentStyleTest.puml new file mode 100644 index 0000000..863a78e --- /dev/null +++ b/src/test/resources/expected/view/style/element/ComponentStyleTest.puml @@ -0,0 +1,14 @@ +@startuml(id=ComponentStyleTest) +!includeurl https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml +title My Software System - My Container - Components +caption ComponentStyleTest + +SHOW_PERSON_OUTLINE() +LAYOUT_TOP_DOWN() + +AddElementTag(Component Style, $sprite="&compass", $bgColor=#ffffff, $fontColor=#ff0000, $borderColor=#800080, $borderStyle=DottedLine(), $borderThickness=5, $shadowing=false, $shape=RoundedBoxShape(), $techn=REST, $legendSprite="&compass", $legendText=this is a legend text) +Component(MySoftwareSystem.MyContainer.MyComponent, "My Component", "", "component", "", $tags="Component Style") + +SHOW_LEGEND(true) + +@enduml \ No newline at end of file diff --git a/src/test/resources/expected/view/style/element/ContainerStyleTest.puml b/src/test/resources/expected/view/style/element/ContainerStyleTest.puml new file mode 100644 index 0000000..e8c6b5b --- /dev/null +++ b/src/test/resources/expected/view/style/element/ContainerStyleTest.puml @@ -0,0 +1,22 @@ +@startuml(id=ContainerStyleTest) +!includeurl https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml +!includeurl https://raw.githubusercontent.com/plantuml-stdlib/gilbarbara-plantuml-sprites/master/sprites/postgresql.puml +title My Software System - Containers +caption ContainerStyleTest + +SHOW_PERSON_OUTLINE() +LAYOUT_TOP_DOWN() + +AddElementTag(Container Style, $sprite="postgresql{scale=0.5,color=#008000}", $bgColor=#ffffff, $fontColor=#ff0000, $borderColor=#800080, $borderStyle=DottedLine(), $borderThickness=5, $shadowing=false, $shape=RoundedBoxShape(), $techn=REST, $legendSprite="&compass", $legendText=this is a legend container) +AddBoundaryTag(System Boundary Style, $bgColor=#00ffff, $fontColor=#008000, $borderColor=#ff0000, $borderStyle=DottedLine(), $borderThickness=4, $shadowing=false, $legendText=this is a system) +AddPersonTag(Person Style, $bgColor=#00ff00, $fontColor=#0000ff, $borderColor=#ff0000, $borderStyle=DashedLine(), $borderThickness=4, $shadowing=false, $shape=EightSidedShape(), $legendText=this is a person) +System_Boundary(MySoftwareSystem, My Software System, $tags="System Style+System Boundary Style") { + Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style+Container Boundary Style") +} +Person(Person, "Person", "", "", $tags="Person Style") + +Rel(Person, MySoftwareSystem.MyContainer, "uses container") + +SHOW_LEGEND(true) + +@enduml \ No newline at end of file diff --git a/src/test/resources/expected/view/style/SystemStyleTest.puml b/src/test/resources/expected/view/style/element/SystemStyleTest.puml similarity index 64% rename from src/test/resources/expected/view/style/SystemStyleTest.puml rename to src/test/resources/expected/view/style/element/SystemStyleTest.puml index 629011f..f832230 100644 --- a/src/test/resources/expected/view/style/SystemStyleTest.puml +++ b/src/test/resources/expected/view/style/element/SystemStyleTest.puml @@ -7,8 +7,11 @@ SHOW_PERSON_OUTLINE() LAYOUT_TOP_DOWN() AddElementTag(System Style, $sprite="img:https://plantuml.com/logo3.png{scale=0.4}", $bgColor=#000000, $fontColor=#ffff00, $borderColor=#008000, $borderStyle=DashedLine(), $borderThickness=4, $shadowing=true, $shape=EightSidedShape(), $techn=Kafka, $legendSprite="&compass{scale=3.0}", $legendText=this is a legend) -System(MySoftwareSystem, "My Software System", "system", "", $tags="System Style" ) +AddPersonTag(Person Style, $bgColor=#00ff00, $fontColor=#0000ff, $borderColor=#ff0000, $borderStyle=DashedLine(), $borderThickness=4, $shadowing=false, $shape=EightSidedShape(), $legendText=this is a legend) +System(MySoftwareSystem, "My Software System", "system", "", $tags="System Style+System Boundary Style") +Person(Person, "Person", "", "", $tags="Person Style") +Rel(Person, MySoftwareSystem, "uses system") SHOW_LEGEND(true) diff --git a/src/test/resources/expected/view/style/ViewStyleTestUnusedTag.puml b/src/test/resources/expected/view/style/element/ViewStyleTestUnusedTag.puml similarity index 84% rename from src/test/resources/expected/view/style/ViewStyleTestUnusedTag.puml rename to src/test/resources/expected/view/style/element/ViewStyleTestUnusedTag.puml index 69bf68d..7057278 100644 --- a/src/test/resources/expected/view/style/ViewStyleTestUnusedTag.puml +++ b/src/test/resources/expected/view/style/element/ViewStyleTestUnusedTag.puml @@ -6,7 +6,7 @@ caption ViewStyleTestWithStyle SHOW_PERSON_OUTLINE() LAYOUT_TOP_DOWN() -Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style" ) +Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style+Container Boundary Style") SHOW_LEGEND(true) diff --git a/src/test/resources/expected/view/style/ViewStyleTestWithStyle.puml b/src/test/resources/expected/view/style/element/ViewStyleTestWithStyle.puml similarity index 92% rename from src/test/resources/expected/view/style/ViewStyleTestWithStyle.puml rename to src/test/resources/expected/view/style/element/ViewStyleTestWithStyle.puml index 58514f5..4a23d56 100644 --- a/src/test/resources/expected/view/style/ViewStyleTestWithStyle.puml +++ b/src/test/resources/expected/view/style/element/ViewStyleTestWithStyle.puml @@ -8,7 +8,7 @@ SHOW_PERSON_OUTLINE() LAYOUT_TOP_DOWN() AddElementTag(Container Style, $sprite="postgresql{scale=0.5,color=#008000}", $bgColor=#ffffff, $fontColor=#ff0000, $borderColor=#800080, $borderStyle=DottedLine(), $borderThickness=5, $shadowing=false, $shape=RoundedBoxShape(), $techn=REST, $legendSprite="&compass", $legendText=this is a legend text) -Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style" ) +Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style+Container Boundary Style") SHOW_LEGEND(true) diff --git a/src/test/resources/expected/view/style/ViewStyleTestWithoutStyle.puml b/src/test/resources/expected/view/style/element/ViewStyleTestWithoutStyle.puml similarity index 84% rename from src/test/resources/expected/view/style/ViewStyleTestWithoutStyle.puml rename to src/test/resources/expected/view/style/element/ViewStyleTestWithoutStyle.puml index ab13619..9d88a5d 100644 --- a/src/test/resources/expected/view/style/ViewStyleTestWithoutStyle.puml +++ b/src/test/resources/expected/view/style/element/ViewStyleTestWithoutStyle.puml @@ -6,7 +6,7 @@ caption ViewStyleTestWithoutStyle SHOW_PERSON_OUTLINE() LAYOUT_TOP_DOWN() -Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style" ) +Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style+Container Boundary Style") SHOW_LEGEND(true) diff --git a/src/test/resources/expected/view/style/person/PersonStyleTest.puml b/src/test/resources/expected/view/style/person/PersonStyleTest.puml new file mode 100644 index 0000000..00bb9e6 --- /dev/null +++ b/src/test/resources/expected/view/style/person/PersonStyleTest.puml @@ -0,0 +1,17 @@ +@startuml(id=PersonStyleTest) +!includeurl https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml +title My Software System - System Context +caption PersonStyleTest + +SHOW_PERSON_OUTLINE() +LAYOUT_TOP_DOWN() + +AddPersonTag(special person, $sprite="img:https://plantuml.com/logo3.png{scale=0.4}", $bgColor=#fbbccf, $fontColor=#ff0000, $borderColor=#0000ff, $borderStyle=DashedLine(), $borderThickness=4, $shadowing=true, $shape=RoundedBoxShape(), $legendSprite="&compass{scale=3.0}", $legendText=this is a person) +System(MySoftwareSystem, "My Software System", "system", "", $tags="System Style") +Person(TestPerson, "Test Person", "", "", $tags="special person", $link="www.google.com") + +Rel(TestPerson, MySoftwareSystem, "uses") + +SHOW_LEGEND(true) + +@enduml \ No newline at end of file