chriskn / structurizr-c4puml-extension

Kotlin based extension for the struturizr java api with extended C4-PlantUML support
23 stars 8 forks source link

LocationExtensionKt throws an exception when elements are in the model that have not been created with this library #236

Closed klu2 closed 2 months ago

klu2 commented 2 months ago

If you add any elements (people, software systems, containers, components) to the model by using the original mehods from Structurizr (i.e. addPerson or addContainer), and you then generate views using workspace.writeDiagrams, you get the following exception:

Key c4location is missing in the map.
java.util.NoSuchElementException: Key c4location is missing in the map.
    at kotlin.collections.MapsKt__MapWithDefaultKt.getOrImplicitDefaultNullable(MapWithDefault.kt:24)
    at kotlin.collections.MapsKt__MapsKt.getValue(Maps.kt:369)
    at com.github.chriskn.structurizrextension.api.model.LocationExtensionKt.getC4Location(LocationExtension.kt:40)
    at com.github.chriskn.structurizrextension.internal.export.writer.ElementWriter.toMacro(ElementWriter.kt:91)
    at com.github.chriskn.structurizrextension.internal.export.writer.ElementWriter.writeElement(ElementWriter.kt:36)
    at com.github.chriskn.structurizrextension.internal.export.ExtendedC4PlantUMLExporter.writeElement(ExtendedC4PlantUMLExporter.kt:52)
    at com.github.chriskn.structurizrextension.internal.export.view.SystemViewExporter.writeElementsOutsideEnterprise(SystemViewExporter.kt:76)
    at com.github.chriskn.structurizrextension.internal.export.view.SystemViewExporter.exportContextView$structurizr_c4puml_extension(SystemViewExporter.kt:58)
    at com.github.chriskn.structurizrextension.internal.export.ExtendedC4PlantUMLExporter.export(ExtendedC4PlantUMLExporter.kt:89)
    at com.structurizr.export.AbstractDiagramExporter.export(AbstractDiagramExporter.java:46)
    at com.github.chriskn.structurizrextension.api.WorkspaceKt.writeDiagrams(Workspace.kt:19)

The reason is that the default value for the element is not set in that case, as this method is not called and therefore person.c4Location is never set:

fun Model.person(
    name: String,
    description: String = "",
    location: Location = Location.Unspecified,
    icon: String? = null,
    link: String? = null,
    tags: List<String> = listOf(),
    properties: C4Properties? = null,
    uses: List<Dependency<StaticStructureElement>> = listOf(),
): Person {
    @Suppress("DEPRECATION")
    val person = this.addPerson(name, description)
    person.c4Location = location
    person.configure(icon, link, tags, properties)
    uses.forEach { dep -> dep.addRelationShipFrom(person) }
    return person
}

And when LocationExtension tries to access that property later during diagram generation, an Exception is thrown.

There are some ways to fix that, but I think easiest would be to return the default value Location.Unspecified inside LocationExtension

chriskn commented 2 months ago

Hi @klu2, thank you for reporting the issue and your PR.

While looking into it, I noticed one more issue when rendering diagrams for model elements created using the structurizr API. So I took the extra mile and wrote a set of tests to ensure all diagram types are rendered properly when using the structurizr model API.

You can find the changes in #238 Since this PR addresses the same issues as your PR I will close your PR and will merge #238