All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4k.tracing.renderer.PumlSequenceDiagram.kt Maven / Gradle / Ivy

The newest version!
package org.http4k.tracing.renderer

import org.http4k.core.Status
import org.http4k.tracing.Actor
import org.http4k.tracing.ActorType
import org.http4k.tracing.ActorType.*
import org.http4k.tracing.BiDirectional
import org.http4k.tracing.FireAndForget
import org.http4k.tracing.RequestResponse
import org.http4k.tracing.StartInteraction
import org.http4k.tracing.StartRendering
import org.http4k.tracing.StopRendering
import org.http4k.tracing.Trace
import org.http4k.tracing.TraceRender
import org.http4k.tracing.TraceRenderer
import org.http4k.tracing.TraceStep

object PumlSequenceDiagram : TraceRenderer {
    override fun render(scenarioName: String, steps: List): TraceRender {
        val actors = steps.filterIsInstance().chronologicalActors()

        return TraceRender(
            "$scenarioName - Sequence",
            "PUML",
            """@startuml
            |title $scenarioName
            |${actors.toActor().joinToString("\n")}
            |${
                steps.joinToString("\n") {
                    when (it) {
                        is RequestResponse -> it.asPumlSequenceDiagram()
                        is BiDirectional -> it.asPumlSequenceDiagram()
                        is FireAndForget -> it.asPumlSequenceDiagram()
                        is StartInteraction -> it.asPumlSequenceDiagram()
                        is StartRendering, is StopRendering -> ""
                    }
                }
            }
@enduml""".trimMargin())
    }

    private fun Iterable.toActor() =
        fold(emptyList()) { acc, next ->
            val nextVal = when (next.type) {
                Database -> "database"
                Human -> "actor"
                System -> "participant"
                Queue -> "queue"
            } + " \"${next.name}\""
            if (acc.contains(nextVal)) acc else acc + nextVal
        }

    private fun Trace.asPumlSequenceDiagram() = when (this) {
        is RequestResponse -> asPumlSequenceDiagram()
        is BiDirectional -> asPumlSequenceDiagram()
        is FireAndForget -> asPumlSequenceDiagram()
    }

    private fun RequestResponse.asPumlSequenceDiagram(): String = """
           |"${origin.name}" -> "${target.name}": $request
           |activate "${target.name}"
           |${children.joinToString("\n") { it.asPumlSequenceDiagram() }}
           |"${target.name}" ${response.toArrow()} "${origin.name}": ${response.toColour()} $response
           |deactivate "${target.name}"
            """.trimMargin()

    private fun String.toColour() = try {
        with(toStatus()) {
            when {
                successful -> ""
                redirection -> ""
                clientError -> ""
                serverError -> ""
                else -> ""
            }
        }
    } catch (e: Exception) {
        ">"
    }

    private fun BiDirectional.asPumlSequenceDiagram(): String = (
        """
           |"${origin.name}" <-> "${target.name}": $request""" +
            if (children.isNotEmpty()) """|activate "${target.name}"
           |${children.joinToString("\n") { it.asPumlSequenceDiagram() }}
           |deactivate "${target.name}"""
            else ""
        ).trimMargin()

    private fun FireAndForget.asPumlSequenceDiagram(): String = (
        """
           |"${origin.name}" -> "${target.name}": $request""" +
            if (children.isNotEmpty()) """
           |${children.joinToString("\n") { it.asPumlSequenceDiagram() }}"""
            else ""
        ).trimMargin()

    private fun StartInteraction.asPumlSequenceDiagram(): String = """

    note over "$origin" : "$origin" $interactionName
    """.trimIndent()

    private fun String.toArrow(): String =
        try {
            with(toStatus()) {
                when {
                    successful -> "-[#DarkGreen]>"
                    redirection -> "-[#DarkBlue]>"
                    clientError -> "X-[#DarkOrange]>"
                    serverError -> "X-[#FireBrick]>"
                    else -> "-[#Black]>"
                }
            }
        } catch (e: Exception) {
            "-->"
        }

    private fun String.toStatus() = Status(
        split(" ").first()
            .filter(Char::isDigit)
            .toInt(), split(" ").last()
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy