Skip to content
This repository has been archived by the owner on Apr 12, 2023. It is now read-only.

Commit

Permalink
Add refresh button (#138)
Browse files Browse the repository at this point in the history
* Cache list topic in cluster scope

* Add refresh button

* Add test refresh button in topic list

* Add test for refresh schema registry list

* Fix PR lint

* Add unit test for register schema

* Remove code duplication

* Rename buttons

* Fix text in button alignment

Co-authored-by: Auto Lint <[email protected]>
  • Loading branch information
andrewinci and Auto Lint committed Nov 15, 2020
1 parent 0891e09 commit d6df59d
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package insulator.integrationtest

import insulator.integrationtest.helpers.IntegrationTestFixture
import insulator.integrationtest.helpers.click
import insulator.integrationtest.helpers.lookupFirst
import insulator.integrationtest.helpers.screenShoot
import insulator.integrationtest.helpers.selectCluster
import insulator.integrationtest.helpers.waitWindowWithTitle
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import javafx.scene.Node
import javafx.scene.control.Button
import javafx.scene.control.Label
import javafx.scene.control.ListView
import kotlinx.coroutines.delay
import tornadofx.CssRule
import tornadofx.Stylesheet.Companion.listView
import kotlin.time.ExperimentalTime

@ExperimentalTime
class RefreshButtonTests : FreeSpec({

"Test refresh topic button" - {
IntegrationTestFixture().use { fixture ->
fixture.startAppWithKafkaCuster("Test cluster")
selectCluster(fixture.currentKafkaCluster)

val mainView = waitWindowWithTitle("Insulator")
suspend fun getListViewItems() = mainView.lookupFirst<ListView<String>>(listView).items

"Refresh topic list" {
val topicName = "test-new-topic"
// create topic
fixture.createTopic(topicName)
delay(1_000)
// the topic shouldn't be visible
getListViewItems().count { it == topicName } shouldBe 0
// click the refresh button wil load the new topic
mainView.lookupFirst<Button>(CssRule.id("button-refresh")).click()
screenShoot("refresh-topic-list")
// the list of topic is now updated
getListViewItems().count { it == topicName } shouldBe 1
mainView.lookupFirst<Label>(CssRule.id("topic-$topicName")).text shouldBe topicName
}

"Refresh schema list" {
val schemaName = "test-new-topic-schema"
// select schema registry
mainView.lookupFirst<Node>(CssRule.id("sidebar-item-schema-registry")).click()
// create the schema
fixture.createTestSchema(schemaName)
delay(1_000)
// the topic shouldn't be visible
getListViewItems().count { it == schemaName } shouldBe 0
// click the refresh button wil load the new topic
mainView.lookupFirst<Button>(CssRule.id("button-refresh")).click()
screenShoot("refresh-schema-registry-list")
// the list of topic is now updated
getListViewItems().count { it == schemaName } shouldBe 1
mainView.lookupFirst<Label>(CssRule.id("schema-$schemaName")).text shouldBe schemaName
}
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package insulator.integrationtest.helpers
import insulator.Insulator
import insulator.configuration.ConfigurationRepo
import insulator.kafka.AdminApi
import insulator.kafka.SchemaRegistry
import insulator.kafka.adminApi
import insulator.kafka.local.SchemaRegistryContainer
import insulator.kafka.model.Cluster
import insulator.kafka.model.SchemaRegistryConfiguration
import insulator.kafka.model.Topic
import insulator.kafka.producer.Producer
import insulator.kafka.producer.stringProducer
import insulator.kafka.schemaRegistry
import insulator.test.helper.deleteTestSandboxFolder
import insulator.test.helper.getTestSandboxFolder
import org.testcontainers.containers.KafkaContainer
Expand All @@ -20,10 +22,11 @@ import java.io.Closeable

class IntegrationTestFixture : Closeable {
private lateinit var adminApi: AdminApi
private lateinit var schemaRegistry: SchemaRegistry
lateinit var stringProducer: Producer
private val currentHomeFolder = getTestSandboxFolder().toAbsolutePath()
private val kafka = KafkaContainer()
private val schemaRegistry = SchemaRegistryContainer().withKafka(kafka)
private val schemaRegistryContainer = SchemaRegistryContainer().withKafka(kafka)
lateinit var currentKafkaCluster: Cluster

init {
Expand All @@ -40,12 +43,13 @@ class IntegrationTestFixture : Closeable {
name = clusterName,
endpoint = kafka.bootstrapServers,
schemaRegistryConfig = if (createSchemaRegistry) {
schemaRegistry.start()
schemaRegistry.waitingFor(Wait.forListeningPort())
SchemaRegistryConfiguration(schemaRegistry.endpoint)
schemaRegistryContainer.start()
schemaRegistryContainer.waitingFor(Wait.forListeningPort())
SchemaRegistryConfiguration(schemaRegistryContainer.endpoint)
} else SchemaRegistryConfiguration()
)
startApp(currentKafkaCluster)
if (createSchemaRegistry) schemaRegistry = schemaRegistry(currentKafkaCluster)
adminApi = adminApi(currentKafkaCluster)
stringProducer = stringProducer(currentKafkaCluster)
}
Expand All @@ -68,8 +72,29 @@ class IntegrationTestFixture : Closeable {
kotlin.runCatching { FxToolkit.cleanupApplication(FX.application) }
deleteTestSandboxFolder()
kafka.runCatching { stop(); close() }
schemaRegistry.runCatching { stop(); close() }
schemaRegistryContainer.runCatching { stop(); close() }
}

suspend fun createTopic(s: String) = adminApi.createTopics(Topic(s, partitionCount = 3, replicationFactor = 1))
fun createTestSchema(schemaName: String) = schemaRegistry.register(schemaName, testSchema)

val testSchema =
"""
{
"type": "record",
"name": "value_test_schema",
"namespace": "com.mycorp.mynamespace",
"doc": "Sample schema to help you get started.",
"fields": [{
"name": "test",
"type": ["null", {
"type": "bytes",
"logicalType": "decimal",
"precision": 5,
"scale": 2
}],
"default": null
}]
}
""".trimIndent()
}
28 changes: 22 additions & 6 deletions app/src/main/kotlin/insulator/ui/component/AppBar.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
package insulator.ui.component

import insulator.ui.style.AppBarStyle
import javafx.beans.value.ObservableStringValue
import javafx.event.EventTarget
import javafx.geometry.Pos
import javafx.scene.layout.Priority
import javafx.scene.control.Button
import tornadofx.addClass
import tornadofx.hgrow
import tornadofx.borderpane
import tornadofx.buttonbar
import tornadofx.vbox

fun EventTarget.appBar(op: EventTarget.() -> Unit) =
vbox(alignment = Pos.TOP_LEFT) {
op()
hgrow = Priority.ALWAYS
data class AppBarBuilder(
var title: String = "",
var subtitle: ObservableStringValue? = null,
var buttons: List<Button> = emptyList()
)

fun EventTarget.appBar(op: AppBarBuilder.() -> Unit) {
val builder = AppBarBuilder().apply(op)
this.borderpane {
center = vbox {
h1(builder.title)
builder.subtitle?.let { subTitle(builder.subtitle!!) }
alignment = Pos.CENTER_LEFT
}
right = buttonbar {
buttons.addAll(builder.buttons)
}
addClass(AppBarStyle.appBar)
}
}
6 changes: 4 additions & 2 deletions app/src/main/kotlin/insulator/ui/style/AppBarStyle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ class AppBarStyle : Stylesheet() {
appBar {
translateY = -theme.viewPadding
borderInsets = multi(box(-theme.viewPadding, -theme.viewPadding, 0.px, -theme.viewPadding))
spacing = 5.0.px
alignment = Pos.CENTER_LEFT
borderColor = multi(box(theme.backgroundColor, theme.backgroundColor, theme.lightGray, theme.backgroundColor))
minHeight = 5.em
}

button {
alignment = Pos.CENTER
}

burgerButton {
padding = box(1.em)
textFill = theme.mainColor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package insulator.viewmodel.main.topic

import insulator.di.ClusterScope
import insulator.di.factories.TopicComponentFactory
import insulator.helper.dispatch
import insulator.helper.runOnFXThread
Expand All @@ -19,6 +20,7 @@ import tornadofx.SortedFilteredList
import tornadofx.whenUndockedOnce
import javax.inject.Inject

@ClusterScope
class ListTopicViewModel @Inject constructor(
val cluster: Cluster,
val adminApi: AdminApi,
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/kotlin/insulator/views/main/MainView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ class MainView @Inject constructor(
button("Info") { action { clusterView.show(false) }; id = "button-cluster-info" }
}
center = vbox {
menuItem("Topics", ICON_TOPICS) { viewModel.setContentList(ListTopicView::class) }
menuItem("Schema Registry", ICON_REGISTRY) { viewModel.setContentList(ListSchemaView::class) }
menuItem("Topics", ICON_TOPICS, "sidebar-item-topics") { viewModel.setContentList(ListTopicView::class) }
menuItem("Schema Registry", ICON_REGISTRY, "sidebar-item-schema-registry") { viewModel.setContentList(ListSchemaView::class) }
}
bottom = hbox(alignment = Pos.CENTER) {
themeButton { themeHelper.dispatch { changeTheme() } }
Expand All @@ -105,12 +105,13 @@ class MainView @Inject constructor(
maxWidth = SIDEBAR_WIDTH
}

private fun EventTarget.menuItem(name: String, icon: String, onClick: () -> Unit) =
private fun EventTarget.menuItem(name: String, icon: String, id: String, onClick: () -> Unit) =
hbox(spacing = 5.0) {
imageview(Image(icon)) { fitHeight = 35.0; fitWidth = 35.0; }
h2(name)
onMouseClicked = EventHandler { onClick() }
addClass(MainViewStyle.sidebarItem)
this.id = id
}

private fun setSize() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import insulator.di.ClusterScope
import insulator.helper.dispatch
import insulator.ui.common.InsulatorView
import insulator.ui.component.appBar
import insulator.ui.component.h1
import insulator.ui.component.searchBox
import insulator.ui.component.subTitle
import insulator.ui.style.ButtonStyle
import insulator.viewmodel.main.schemaregistry.ListSchemaViewModel
import insulator.viewmodel.main.schemaregistry.LoadSchemaListError
import javafx.event.EventTarget
import javafx.scene.control.SelectionMode
import javafx.scene.layout.Priority
import tornadofx.action
import tornadofx.addClass
import tornadofx.bindSelected
import tornadofx.hbox
import tornadofx.button
import tornadofx.label
import tornadofx.listview
import tornadofx.onDoubleClick
Expand All @@ -28,18 +29,17 @@ class ListSchemaView @Inject constructor(

override val root = vbox(spacing = 5.0) {
appBar {
hbox {
h1("Schema registry")
}
subTitle(viewModel.subtitleProperty)
title = "Schema registry"
subtitle = viewModel.subtitleProperty
buttons = listOf(refreshButton())
}
searchBox(viewModel.searchItemProperty, currentView = this@ListSchemaView)
schemasListView()
}

private fun EventTarget.schemasListView() =
listview<String> {
cellFormat { graphic = label(it) }
cellFormat { graphic = label(it) { id = "schema-$it" } }
onDoubleClick { viewModel.dispatch { showSchema() } }
itemsProperty().set(viewModel.filteredSchemasProperty)
bindSelected(viewModel.selectedSchemaProperty)
Expand All @@ -55,4 +55,11 @@ class ListSchemaView @Inject constructor(
else -> viewModel.refresh()
}
}

private fun EventTarget.refreshButton() =
button("Refresh") {
id = "button-refresh"
action { dispatch { viewModel.refresh() } }
addClass(ButtonStyle.blueButton)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import insulator.ui.common.InsulatorTabView
import insulator.ui.component.appBar
import insulator.ui.component.confirmationButton
import insulator.ui.component.fieldName
import insulator.ui.component.h1
import insulator.ui.style.theme
import insulator.viewmodel.main.schemaregistry.SchemaViewModel
import javafx.event.EventTarget
Expand Down Expand Up @@ -36,10 +35,8 @@ class SchemaView @Inject constructor(

override val root = vbox {
appBar {
hbox(alignment = Pos.CENTER_LEFT, spacing = 5.0) {
h1(viewModel.nameProperty.value)
deleteButton()
}
title = viewModel.nameProperty.value
buttons = listOf(deleteButton())
}
hbox(alignment = Pos.CENTER_LEFT) {
fieldName("Schema")
Expand All @@ -59,7 +56,7 @@ class SchemaView @Inject constructor(
}

private fun EventTarget.deleteButton() =
confirmationButton("delete", "The schema \"${viewModel.nameProperty.value}\" will be removed.") {
confirmationButton("Delete", "The schema \"${viewModel.nameProperty.value}\" will be removed.") {
viewModel.delete()
closeTab()
}
Expand Down
23 changes: 16 additions & 7 deletions app/src/main/kotlin/insulator/views/main/topic/ListTopicView.kt
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
package insulator.views.main.topic

import insulator.di.ClusterScope
import insulator.helper.dispatch
import insulator.ui.common.InsulatorView
import insulator.ui.component.action
import insulator.ui.component.appBar
import insulator.ui.component.h1
import insulator.ui.component.searchBox
import insulator.ui.component.subTitle
import insulator.ui.style.ButtonStyle
import insulator.viewmodel.main.topic.ListTopicViewModel
import insulator.views.configurations.ListClusterView
import javafx.event.EventTarget
import javafx.geometry.Pos
import javafx.scene.control.SelectionMode
import javafx.scene.layout.Priority
import tornadofx.action
import tornadofx.addClass
import tornadofx.bindSelected
import tornadofx.borderpane
import tornadofx.button
import tornadofx.hbox
import tornadofx.label
import tornadofx.listview
import tornadofx.vbox
import tornadofx.vgrow
import javax.inject.Inject

@ClusterScope
class ListTopicView @Inject constructor(override val viewModel: ListTopicViewModel) : InsulatorView("Topics") {

override val root = vbox(spacing = 5.0) {
appBar {
hbox {
h1("Topics")
}
subTitle(viewModel.subtitleProperty)
title = "Topics"
subtitle = viewModel.subtitleProperty
buttons = listOf(refreshButton())
}
borderpane {
left = createTopicButton()
Expand All @@ -57,6 +58,14 @@ class ListTopicView @Inject constructor(override val viewModel: ListTopicViewMod
id = "button-create-topic"
}

private fun EventTarget.refreshButton() =
button("Refresh") {
action { viewModel.dispatch { viewModel.refresh() } }
addClass(ButtonStyle.blueButton)
alignment = Pos.CENTER
id = "button-refresh"
}

override fun onError(throwable: Throwable) {
replaceWith<ListClusterView>()
}
Expand Down
Loading

0 comments on commit d6df59d

Please sign in to comment.