From 6f5aa2663f7501351da343c5dd5df018511449ed Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 13 Oct 2023 15:22:57 +0200 Subject: [PATCH 1/4] Replace deprecated assertk usages Also enhance software system relation test. --- .../site/model/FaviconViewModelTest.kt | 3 +- ...wareSystemDependenciesPageViewModelTest.kt | 46 ++++++++----------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/FaviconViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/FaviconViewModelTest.kt index ab1fe611..1e648844 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/FaviconViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/FaviconViewModelTest.kt @@ -1,5 +1,6 @@ package nl.avisi.structurizr.site.generatr.site.model +import assertk.assertFailure import assertk.assertThat import assertk.assertions.* import kotlin.test.Test @@ -27,7 +28,7 @@ class FaviconViewModelTest : ViewModelTest() { "favicon" ) - assertThat { faviconViewModel() }.isFailure().hasMessage("Favicon must be a valid *.ico, *.png of *.gif file") + assertFailure { faviconViewModel() }.hasMessage("Favicon must be a valid *.ico, *.png of *.gif file") } @Test diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt index 680d0294..c4af1d53 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt @@ -80,10 +80,16 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { backend2.uses(softwareSystem1, "Uses from container 2 to system 1", "REST") softwareSystem1.uses(backend2, "Uses from system 1 to container 2", "REST") - assertThat { SoftwareSystemDependenciesPageViewModel(generatorContext, softwareSystem1) } - .isSuccess() + val viewModel = SoftwareSystemDependenciesPageViewModel(generatorContext, softwareSystem1) + // Inbound Table + assertThat(viewModel.dependenciesInboundTable.bodyRows.extractTitle()) + .containsExactly("Software system 2") + // Outbound Table + assertThat(viewModel.dependenciesOutboundTable.bodyRows.extractTitle()) + .containsExactly("Software system 2") } + @Test fun `dependencies from and to external systems`() { val externalSystem = generatorContext.workspace.model @@ -107,31 +113,11 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { val viewModel = SoftwareSystemDependenciesPageViewModel(generatorContext, softwareSystem1) // Inbound Table - assertThat( - viewModel.dependenciesInboundTable.bodyRows - .map { - when (val source = it.columns[0]) { - is TableViewModel.TextCellViewModel -> source.title - is TableViewModel.LinkCellViewModel -> source.link.title - is TableViewModel.ExternalLinkCellViewModel -> source.link.title - } - } - ).containsExactly( - "Software system 2", "Software system 3" - ) + assertThat(viewModel.dependenciesInboundTable.bodyRows.extractTitle()) + .containsExactly("Software system 2", "Software system 3") // Outbound Table - assertThat( - viewModel.dependenciesInboundTable.bodyRows - .map { - when (val source = it.columns[0]) { - is TableViewModel.TextCellViewModel -> source.title - is TableViewModel.LinkCellViewModel -> source.link.title - is TableViewModel.ExternalLinkCellViewModel -> source.link.title - } - } - ).containsExactly( - "Software system 2", "Software system 3" - ) + assertThat(viewModel.dependenciesInboundTable.bodyRows.extractTitle()) + .containsExactly("Software system 2", "Software system 3") } private fun TableViewModel.TableViewInitializerContext.dependenciesTableHeader() { @@ -141,4 +127,12 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { headerCell("Technology"), ) } + + private fun List.extractTitle() = map { + when (val source = it.columns[0]) { + is TableViewModel.TextCellViewModel -> source.title + is TableViewModel.LinkCellViewModel -> source.link.title + is TableViewModel.ExternalLinkCellViewModel -> source.link.title + } + } } From 92457e2fddcb7cfd86f13572834543a70be6e034 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 13 Oct 2023 15:23:18 +0200 Subject: [PATCH 2/4] Update assertk --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 001247a5..adbb59eb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,7 +43,7 @@ dependencies { testImplementation(kotlin("test")) testImplementation("org.junit.jupiter:junit-jupiter-params") - testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.26.1") + testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.27.0") } application { From dc35b1aad93147f59c76f981738df02e765af7d0 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 13 Oct 2023 15:24:37 +0200 Subject: [PATCH 3/4] Update Jetty from 11 to 12 Include migration of our embedded server implementation. --- build.gradle.kts | 5 +- .../structurizr/site/generatr/ServeCommand.kt | 70 +++++++++++-------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index adbb59eb..18278ed0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,9 +32,8 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.9.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") - implementation("org.eclipse.jetty:jetty-server:11.0.17") - implementation("org.eclipse.jetty:jetty-servlet:11.0.17") - implementation("org.eclipse.jetty.websocket:websocket-jetty-server:11.0.17") + implementation("org.eclipse.jetty:jetty-server:12.0.2") + implementation("org.eclipse.jetty.websocket:jetty-websocket-jetty-server:12.0.2") runtimeOnly("org.slf4j:slf4j-simple:2.0.9") runtimeOnly("org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.10") diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt index f760e2ec..59a77912 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt @@ -3,20 +3,23 @@ package nl.avisi.structurizr.site.generatr import com.sun.nio.file.SensitivityWatchEventModifier -import jakarta.servlet.ServletContext import kotlinx.cli.* import nl.avisi.structurizr.site.generatr.site.copySiteWideAssets import nl.avisi.structurizr.site.generatr.site.generateDiagrams import nl.avisi.structurizr.site.generatr.site.generateRedirectingIndexPage import nl.avisi.structurizr.site.generatr.site.generateSite import org.eclipse.jetty.server.Server -import org.eclipse.jetty.servlet.DefaultServlet -import org.eclipse.jetty.servlet.ServletContextHandler -import org.eclipse.jetty.servlet.ServletHolder +import org.eclipse.jetty.server.handler.ContextHandler +import org.eclipse.jetty.server.handler.ContextHandlerCollection +import org.eclipse.jetty.server.handler.ResourceHandler +import org.eclipse.jetty.util.resource.ResourceFactory +import org.eclipse.jetty.websocket.api.Callback import org.eclipse.jetty.websocket.api.Session -import org.eclipse.jetty.websocket.api.WebSocketAdapter -import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer -import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen +import org.eclipse.jetty.websocket.api.annotations.WebSocket +import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler import java.io.File import java.nio.file.* import java.time.Duration @@ -104,33 +107,34 @@ class ServeCommand : Subcommand("serve", "Start a development server") { Server(port).also { server -> println("Starting server...") - server.handler = createServletContextHandler() + server.handler = createRootContextHandler(server) server.start() println("Server started") println("Open http://localhost:$port in your browser to view the site") } - private fun createServletContextHandler() = - ServletContextHandler().apply { - contextPath = "/" - addServlet(createStaticResourceServlet(), "/*") - addWebSocketServlet(this, "/_events") + private fun createRootContextHandler(server: Server) = ContextHandlerCollection( + ContextHandler(createStaticResourceHandler(), "/"), + ContextHandler("/").apply { + handler = createWebSocketHandler(server, this, "/_events") } + ) - private fun createStaticResourceServlet() = - ServletHolder("default", DefaultServlet()).apply { - setInitParameter("resourceBase", siteDir) + private fun createStaticResourceHandler() = + ResourceHandler().apply { + baseResource = ResourceFactory.of(this).newResource(siteDir) } - private fun addWebSocketServlet( - context: ServletContextHandler, + private fun createWebSocketHandler( + server: Server, + context: ContextHandler, @Suppress("SameParameterValue") pathSpec: String ) = - JettyWebSocketServletContainerInitializer - .configure(context) { _: ServletContext?, container: JettyWebSocketServerContainer -> + WebSocketUpgradeHandler.from(server, context) + .configure { container -> container.idleTimeout = Duration.ZERO - container.addMapping(pathSpec) { _, _ -> EventSocket() } + container.addMapping(pathSpec) { _, _, _ -> EventSocket() } } private fun startWatchService(): WatchService { @@ -199,24 +203,34 @@ class ServeCommand : Subcommand("serve", "Start a development server") { eventSockets.forEach { it.send(message) } } - private inner class EventSocket : WebSocketAdapter() { - override fun onWebSocketConnect(sess: Session?) { - super.onWebSocketConnect(sess) + @WebSocket + inner class EventSocket { + private var session: Session? = null + + @OnWebSocketOpen + fun onWebSocketConnect(session: Session?) { synchronized(eventSocketsLock) { eventSockets.add(this) } + this.session = session updateSiteError?.let { send(it) } } - override fun onWebSocketClose(statusCode: Int, reason: String?) { + @OnWebSocketClose + fun onWebSocketClose(statusCode: Int, reason: String?) { + session = null synchronized(eventSocketsLock) { eventSockets.remove(this) } } - override fun onWebSocketError(cause: Throwable?) { + @OnWebSocketError + fun onWebSocketError(cause: Throwable?) { + session = null synchronized(eventSocketsLock) { eventSockets.remove(this) } } fun send(message: String) { - if (session != null && session.isOpen) - this.session.remote?.sendString(message) + session?.let { + if (it.isOpen) + it.sendText(message, Callback.NOOP) + } } } } From 812f59d8308502cc0dea9f0c6faf2d216f77701c Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Tue, 17 Oct 2023 15:37:10 +0200 Subject: [PATCH 4/4] Fix formatting --- .../site/model/SoftwareSystemDependenciesPageViewModelTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt index c4af1d53..b2a6a128 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt @@ -89,7 +89,6 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { .containsExactly("Software system 2") } - @Test fun `dependencies from and to external systems`() { val externalSystem = generatorContext.workspace.model