Skip to content

Commit

Permalink
tests
Browse files Browse the repository at this point in the history
cleaning up header writer
renaming SpriteRegistry.kt to SpriteLibrary.kt
  • Loading branch information
chriskn committed Nov 29, 2024
1 parent 13c8f29 commit a0dd990
Show file tree
Hide file tree
Showing 47 changed files with 17,746 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ data class ImageSprite(
val url: String,
val scale: Double? = null,
override val name: String? = null,
val additionalDefinitions: List<String>? = null,
val additionalDefinitions: Set<String>? = null,
) : Sprite(name, scale) {

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ data class PlantUmlSprite(
val reference: String = defaultReference(path),
val color: String? = null,
val scale: Double? = null,
val additionalIncludes: List<String>? = null,
val additionalDefinitions: List<String>? = null,
val additionalIncludes: Set<String>? = null,
val additionalDefinitions: Set<String>? = null,
) : Sprite(name, scale) {

companion object {
private const val PUML_FILE_EXTENSION = ".puml"
private const val PLANT_UML_FILE_EXTENSION = ".puml"
private fun defaultReference(path: String): String = path
.substringAfterLast("/")
.replace(PUML_FILE_EXTENSION, "")
.replace(PLANT_UML_FILE_EXTENSION, "")
.replace(">", "")
}

Expand All @@ -43,13 +43,15 @@ data class PlantUmlSprite(

init {
require(name.isNotBlank()) { "Icon name must not be blank" }
validateUrl(path)
additionalIncludes?.forEach { validateUrl(it) }
validatePath(path)
additionalIncludes?.forEach { validatePath(it) }
}

private fun validateUrl(url: String) {
require(url.endsWith(PUML_FILE_EXTENSION) || (url.startsWith("<") && url.endsWith(">"))) {
"Icon URL needs to point to $PUML_FILE_EXTENSION file or must be a part of the Plantuml StdLib but was $url"
private fun validatePath(path: String) {
val pathIsStandardLibReference = path.startsWith("<") && path.endsWith(">")
val pathPointsToPumlFile = path.endsWith(PLANT_UML_FILE_EXTENSION)
require(pathPointsToPumlFile || pathIsStandardLibReference) {
"Icon URL needs to point to $PLANT_UML_FILE_EXTENSION file or must be a reference to the Plantuml StdLib but was $path"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ sealed class Sprite(open val name: String? = null, scale: Double?) {
}

internal fun additionalDefinitions(): List<String> = when (this) {
is PlantUmlSprite -> this.additionalDefinitions.orEmpty()
is ImageSprite -> this.additionalDefinitions.orEmpty()
is PlantUmlSprite -> this.additionalDefinitions.orEmpty().toList()
is ImageSprite -> this.additionalDefinitions.orEmpty().toList()
is OpenIconicSprite -> emptyList()
}

internal fun additionalIncludes(): List<String> = when (this) {
is PlantUmlSprite -> this.additionalIncludes.orEmpty()
is PlantUmlSprite -> this.additionalIncludes.orEmpty().toList()
is ImageSprite -> emptyList()
is OpenIconicSprite -> emptyList()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.github.chriskn.structurizrextension.api.view.sprite.library

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.github.chriskn.structurizrextension.api.view.sprite.ImageSprite
import com.github.chriskn.structurizrextension.api.view.sprite.PlantUmlSprite
import com.github.chriskn.structurizrextension.api.view.sprite.Sprite
import java.net.URI
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.toPath

/**
* Sprite library
*
* Library offering sprites from different [SpriteSet]s associated by their name.
*
* Allows to load json SpriteSets from URLs.
*/
object SpriteLibrary {

private const val DEFAULT_SPRITES_FOLDER = "/sprites/"

private val spritesByName: MutableMap<String, Sprite> = mutableMapOf()

private val defaultSpriteSetPaths = this.javaClass.getResource(DEFAULT_SPRITES_FOLDER)
?.toURI()
?.toPath()
?.listDirectoryEntries()
.orEmpty()

init {
defaultSpriteSetPaths.map { spriteSetPath ->
loadSpriteSet(spriteSetPath.toUri())
}
}

/**
* Get Sprite by name
*
* @param name the name of the sprite.
* @return the sprite with the given name
*
* @throws IllegalArgumentException if sprite with name does not exist
*/
fun spriteByName(name: String): Sprite {
val lowercaseName = name.lowercase()
return spritesByName[lowercaseName]
?: throw IllegalArgumentException(
"No sprite found for name $lowercaseName. Possible matches: ${
findSpriteByNameContaining(Regex(lowercaseName))
.map { it.name }
.joinToString(", ")
}"
)
}

/**
* Get Sprite by name or null
*
* @param name the name of the sprite.
* @return the sprite with the given name or null if no sprite with name exists
*/
fun spriteByNameOrNull(name: String): Sprite? {
val lowercaseName = name.lowercase()
return spritesByName[lowercaseName]
}

/**
* Find Sprite by name containing regex
*
* @param nameRegex the regex applied to all sprite names
* @return all sprites with name containing nameRegex
*/
fun findSpriteByNameContaining(nameRegex: Regex): List<Sprite> = spritesByName
.filter { it.key.contains(nameRegex) }
.values
.toList()

/**
* Load sprite set
*
* Loads a [SpriteSet] json from the given URL and adds the contained sprites to the library
*
* @param spriteSetJsonUri URI pointing to [SpriteSet] json file
* @return the loaded SpriteSet
*/
fun loadSpriteSet(spriteSetJsonUri: URI): SpriteSet {
val spriteSet = jacksonObjectMapper().readValue(spriteSetJsonUri.toURL(), SpriteSet::class.java)
val configuredSprites = spriteSet.sprites.map { sprite ->
configureSprite(sprite, spriteSet)
}.toSet()
addSpritesByName(configuredSprites)
return spriteSet.copy(sprites = configuredSprites)
}

private fun addSpritesByName(sprites: Set<Sprite>) {
val spritesWithName = sprites.filter { it.name != null }
spritesByName.putAll(spritesWithName.associateBy { it.name!!.lowercase() })
}

private fun configureSprite(
sprite: Sprite,
spriteSet: SpriteSet,
) = when (sprite) {
is PlantUmlSprite -> sprite.copy(
additionalIncludes = spriteSet.additionalIncludes.orEmpty() + sprite.additionalIncludes.orEmpty(),
additionalDefinitions = spriteSet.additionalDefinitions.orEmpty() + sprite.additionalDefinitions.orEmpty()
)

is ImageSprite -> sprite.copy(
additionalDefinitions = spriteSet.additionalDefinitions.orEmpty() + sprite.additionalDefinitions.orEmpty()
)

else -> sprite
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.github.chriskn.structurizrextension.api.view.sprite.registry
package com.github.chriskn.structurizrextension.api.view.sprite.library

import com.github.chriskn.structurizrextension.api.view.sprite.Sprite

/**
* SpriteSet
*
* Used to describe a set of sprites as json and load it via [SpriteRegistry]
* Used to describe a set of sprites as json and load it via [SpriteLibrary]
*
* @property name name of the SpriteSet. Should describe the contained sprites
* @property source optional url or other pointer to the original source of the sprites contained
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ internal class HeaderWriter(private val styleWriter: StyleWriter) {
// Spaces in PlantUML ids can cause issues. Alternatively, id can be surrounded with double quotes
writer.writeLine("@startuml(id=${view.key.replace(' ', '_')})")

val c4PumlIncludeURI = "$C4_PLANT_UML_STDLIB_URL/${includeForView(view)}"
val iconIncludes = addIncludeUrlsForIcons(view)
val dependencyStyles = styleWriter.collectAppliedDependencyStyles(view)
val personStyles = styleWriter.collectAppliedPersonStyles(view)
Expand All @@ -56,7 +57,7 @@ internal class HeaderWriter(private val styleWriter: StyleWriter) {
val spriteIncludes = sprites.filterIsInstance<PlantUmlSprite>().map { it.path }.toSortedSet()
val spriteAdditionalIncludes = sprites.map { it.additionalIncludes() }.flatten().toSortedSet().toList()
val allSpriteIncludes = spriteAdditionalIncludes + spriteIncludes
val includes = allSpriteIncludes + iconIncludes
val includes = listOf(c4PumlIncludeURI) + allSpriteIncludes + iconIncludes
includes.forEach {
writer.writeLine("!includeurl $it")
}
Expand Down Expand Up @@ -138,8 +139,6 @@ internal class HeaderWriter(private val styleWriter: StyleWriter) {
}

includeUrlsForElements.forEach { includes.add(it) }
val c4PumlIncludeURI = "$C4_PLANT_UML_STDLIB_URL/${includeForView(view)}"
includes.add(c4PumlIncludeURI)
return includes.toSortedSet()
}

Expand Down
10,306 changes: 10,305 additions & 1 deletion src/main/resources/sprites/aws_stdlib_sprites.json

Large diffs are not rendered by default.

7,010 changes: 7,009 additions & 1 deletion src/main/resources/sprites/gilbarbara_image_sprites.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.github.chriskn.structurizrextension.api.view.layout.Legend.ShowLegend
import com.github.chriskn.structurizrextension.api.view.layout.LineType.Ortho
import com.github.chriskn.structurizrextension.api.view.showExternalSoftwareSystemBoundaries
import com.github.chriskn.structurizrextension.api.view.sprite.PlantUmlSprite
import com.github.chriskn.structurizrextension.api.view.sprite.registry.SpriteRegistry
import com.github.chriskn.structurizrextension.api.view.sprite.library.SpriteLibrary
import com.github.chriskn.structurizrextension.assertExpectedDiagramWasWrittenForView
import com.structurizr.Workspace
import com.structurizr.model.InteractionStyle.Asynchronous
Expand All @@ -42,23 +42,23 @@ class ContainerViewTest {
name = "Backend App",
description = "some backend app",
technology = "Kotlin",
sprite = SpriteRegistry.spriteByName("logos-docker-icon"),
sprite = SpriteLibrary.spriteByName("logos-docker-icon"),
link = "https://www.google.de",
properties = properties
)
private val app = softwareSystem.container(
name = "App",
description = "Azure app",
technology = "Some Service",
sprite = (SpriteRegistry.spriteByName("Azure-AIMachineLearning-AzureTranslatorText") as PlantUmlSprite).copy(
sprite = (SpriteLibrary.spriteByName("Azure-AIMachineLearning-AzureTranslatorText") as PlantUmlSprite).copy(
color = "white"
),
)
private val graphql = model.softwareSystem(
name = "GraphQL",
description = "Federated GraphQL",
location = Location.External,
sprite = SpriteRegistry.spriteByName("logos-graphql"),
sprite = SpriteLibrary.spriteByName("logos-graphql"),
)
private val internalSchema = graphql.container(
name = "Internal Schema",
Expand All @@ -70,7 +70,7 @@ class ContainerViewTest {
app,
"request data using",
"GraphQL",
sprite = SpriteRegistry.spriteByName("tupadr3-devicons2-graphql"),
sprite = SpriteLibrary.spriteByName("tupadr3-devicons2-graphql"),
link = "https://graphql.org/"
)
)
Expand All @@ -85,13 +85,13 @@ class ContainerViewTest {
description = "Message Broker",
location = Location.External,
c4Type = C4Type.QUEUE,
sprite = SpriteRegistry.spriteByName("logos-kafka"),
sprite = SpriteLibrary.spriteByName("logos-kafka"),
)
private val topic = broker.container(
"my.topic",
"external topic",
c4Type = C4Type.QUEUE,
sprite = SpriteRegistry.spriteByName("logos-kafka-img"),
sprite = SpriteLibrary.spriteByName("logos-kafka-img"),
usedBy = listOf(
Dependency(backendApplication, "reads topic", "Avro", interactionStyle = Asynchronous)
)
Expand All @@ -101,7 +101,7 @@ class ContainerViewTest {
description = "some database",
c4Type = C4Type.DATABASE,
technology = "postgres",
sprite = SpriteRegistry.spriteByName("logos-postgresql-img"),
sprite = SpriteLibrary.spriteByName("logos-postgresql-img"),
usedBy = listOf(Dependency(backendApplication, "CRUD", "JPA"))
)

Expand All @@ -121,7 +121,7 @@ class ContainerViewTest {
name = "Android User",
description = "some Android user",
location = Location.External,
sprite = SpriteRegistry.spriteByName("logos-android-icon"),
sprite = SpriteLibrary.spriteByName("logos-android-icon"),
uses = listOf(Dependency(app, "uses app"))
)
model.softwareSystem(
Expand Down
Loading

0 comments on commit a0dd990

Please sign in to comment.