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

Commit

Permalink
Feat/delete schema from schema registry (#48)
Browse files Browse the repository at this point in the history
* Add delete button

* Add logic to delete the schema from the registry

* Add happy path test

* Add schema list viewmodel
  • Loading branch information
andrewinci authored Sep 11, 2020
1 parent d71d96a commit c7e7c25
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 38 deletions.
7 changes: 7 additions & 0 deletions src/main/kotlin/insulator/lib/kafka/SchemaRegistry.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ package insulator.lib.kafka

import arrow.core.Either
import arrow.core.extensions.fx
import arrow.core.left
import arrow.core.right
import insulator.lib.helpers.toEither
import insulator.lib.kafka.model.Schema
import insulator.lib.kafka.model.Subject
import io.confluent.kafka.schemaregistry.client.SchemaRegistryClient

class SchemaRegistry(private val client: SchemaRegistryClient) {

fun deleteSubject(subject: String) =
client.runCatching { deleteSubject(subject) }
.fold({ Unit.right() }, { it.left() })

fun getAllSubjects(): Either<Throwable, Collection<String>> =
client.runCatching { allSubjects.sorted() }.toEither()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
package insulator.viewmodel.main.schemaregistry

import arrow.core.extensions.either.applicativeError.handleError
import insulator.lib.helpers.runOnFXThread
import insulator.lib.kafka.SchemaRegistry
import insulator.viewmodel.common.InsulatorViewModel
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import tornadofx.ViewModel

class ListSchemaViewModel : ViewModel() {
class ListSchemaViewModel : InsulatorViewModel() {

private val schemaRegistryClient: SchemaRegistry by di()

fun listSchemas(): ObservableList<String> = schemaRegistryClient.getAllSubjects()
.map { FXCollections.observableList(it.toList()) }
.fold({ throw it }, { it })
val listSchema: ObservableList<String> = FXCollections.observableArrayList<String>()

init { refresh() }

fun getSchema(subject: String) = schemaRegistryClient.getSubject(subject)
.map { SchemaViewModel(it) }
.fold({ throw it }, { it })

fun refresh() = schemaRegistryClient
.getAllSubjects()
.map { it.sorted() }
.map {
it.runOnFXThread {
listSchema.clear()
listSchema.addAll(it)
}
}.handleError {
error.set(it)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package insulator.viewmodel.main.schemaregistry

import insulator.lib.jsonhelper.JsonFormatter
import insulator.lib.jsonhelper.Token
import insulator.lib.kafka.SchemaRegistry
import insulator.lib.kafka.model.Subject
import javafx.beans.property.Property
import javafx.beans.property.SimpleObjectProperty
Expand All @@ -12,8 +13,8 @@ import tornadofx.ViewModel
import tornadofx.onChange

class SchemaViewModel(schema: Subject) : ViewModel() {

private val formatter: JsonFormatter by di()
private val schemaRegistry: SchemaRegistry by di()

val nameProperty = SimpleStringProperty(schema.subject)
val versionsProperty: ObservableList<Int> = FXCollections.observableArrayList<Int>(schema.schemas.map { it.version })
Expand All @@ -29,4 +30,6 @@ class SchemaViewModel(schema: Subject) : ViewModel() {
}
selectedVersionProperty.value = schema.schemas.last().version
}

fun delete() = schemaRegistry.deleteSubject(nameProperty.value)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ class ListSchemaView : View("Schema registry") {
if (this.selectedItem == null) return@onDoubleClick
val scope = Scope()
tornadofx.setInScope(viewModel.getSchema(this.selectedItem!!), scope)
find<SchemaView>(scope).openWindow()
find<SchemaView>(scope).also { it.whenUndockedOnce { viewModel.refresh() } }.openWindow()
}
runAsync {
itemsProperty().set(
SortedFilteredList(viewModel.listSchemas()).apply {
SortedFilteredList(viewModel.listSchema).apply {
filterWhen(searchItem) { p, i -> i.toLowerCase().contains(p.toLowerCase()) }
}.filteredItems
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import insulator.styles.Controls
import insulator.styles.Theme
import insulator.styles.Titles
import insulator.viewmodel.main.schemaregistry.SchemaViewModel
import javafx.event.EventTarget
import javafx.geometry.Insets
import javafx.geometry.Pos
import javafx.scene.control.Alert
import javafx.scene.control.ButtonType
import javafx.scene.input.Clipboard
import javafx.scene.layout.Background
import javafx.scene.layout.BackgroundFill
Expand All @@ -22,8 +25,9 @@ class SchemaView : View("Schema registry") {

override val root = borderpane {
top = vbox {
vbox {
hbox {
label(viewModel.nameProperty.value) { addClass(Titles.h1) }
deleteButton()
addClass(Controls.topBarMenu)
}
hbox { addClass(Controls.topBarMenuShadow) }
Expand Down Expand Up @@ -66,6 +70,30 @@ class SchemaView : View("Schema registry") {
addClass(Controls.view)
}

private fun EventTarget.deleteButton() = apply {
button("Delete") {
addClass(Controls.alertButton)
action {
val closeWindow = { close() }
alert(
Alert.AlertType.WARNING,
"All versions of the schema \"${viewModel.nameProperty.value}\" will be removed.", null,
ButtonType.CANCEL, ButtonType.OK,
owner = currentWindow,
actionFn = { buttonType ->
when (buttonType) {
ButtonType.OK -> {
viewModel.delete()
closeWindow()
}
else -> Unit
}
}
)
}
}
}

override fun onDock() {
super.currentStage?.width = 800.0
super.currentStage?.height = 800.0
Expand Down
9 changes: 3 additions & 6 deletions src/main/kotlin/insulator/views/main/topic/ListTopicView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ class ListTopicView : InsulatorView<ListTopicViewModel>("Topics", ListTopicViewM
action {
val scope = Scope()
setInScope(CreateTopicViewModel(), scope)
find<CreateTopicView>(scope).also {
it.whenUndockedOnce { viewModel.refresh() }
}.openWindow(StageStyle.UTILITY, Modality.WINDOW_MODAL)
find<CreateTopicView>(scope).also { it.whenUndockedOnce { viewModel.refresh() } }
.openWindow(StageStyle.UTILITY, Modality.WINDOW_MODAL)
}
}
}
Expand All @@ -38,9 +37,7 @@ class ListTopicView : InsulatorView<ListTopicViewModel>("Topics", ListTopicViewM
if (this.selectedItem == null) return@onDoubleClick
val scope = Scope()
tornadofx.setInScope(TopicViewModel(this.selectedItem!!), scope)
find<TopicView>(scope).also {
it.whenUndockedOnce { viewModel.refresh() }
}.openWindow()
find<TopicView>(scope).also { it.whenUndockedOnce { viewModel.refresh() } }.openWindow()
}
itemsProperty().set(
SortedFilteredList(viewModel.topicList).apply {
Expand Down
47 changes: 26 additions & 21 deletions src/main/kotlin/insulator/views/main/topic/TopicView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import javafx.beans.binding.Bindings
import javafx.beans.property.SimpleStringProperty
import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections
import javafx.event.EventTarget
import javafx.geometry.Pos
import javafx.scene.control.Alert
import javafx.scene.control.ButtonType
Expand Down Expand Up @@ -45,27 +46,7 @@ class TopicView : View() {
vbox {
hbox(spacing = 10.0, alignment = Pos.CENTER_LEFT) {
label(viewModel.nameProperty.value) { addClass(Titles.h1) }
button("Delete") {
addClass(Controls.alertButton)
action {
val closeWindow = { close() }
alert(
Alert.AlertType.WARNING,
"The topic \"${viewModel.nameProperty.value}\" will be removed.", null,
ButtonType.CANCEL, ButtonType.OK,
owner = currentWindow,
actionFn = { buttonType ->
when (buttonType) {
ButtonType.OK -> {
viewModel.delete()
closeWindow()
}
else -> Unit
}
}
)
}
}
deleteButton()
}
label(subtitleProperty) { addClass(Titles.h3) }
addClass(Controls.topBarMenu, Titles.subtitle)
Expand Down Expand Up @@ -126,6 +107,30 @@ class TopicView : View() {
addClass(Controls.view)
}

private fun EventTarget.deleteButton() = apply {
button("Delete") {
addClass(Controls.alertButton)
action {
val closeWindow = { close() }
alert(
Alert.AlertType.WARNING,
"The topic \"${viewModel.nameProperty.value}\" will be removed.", null,
ButtonType.CANCEL, ButtonType.OK,
owner = currentWindow,
actionFn = { buttonType ->
when (buttonType) {
ButtonType.OK -> {
viewModel.delete()
closeWindow()
}
else -> Unit
}
}
)
}
}
}

private fun TableView<RecordViewModel>.recordList() =
SortedFilteredList(viewModel.records).apply {
filterWhen(searchItem) { p, i ->
Expand Down
16 changes: 14 additions & 2 deletions src/test/kotlin/insulator/lib/kafka/SchemaRegistryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import io.mockk.mockk

class SchemaRegistryTest : FunSpec({

test("getAllSubjects") {
test("happy path getAllSubjects") {
// arrange
val subjects = listOf("subject1", "subject2")
val mockSchema = mockk<SchemaRegistryClient> {
Expand All @@ -21,7 +21,7 @@ class SchemaRegistryTest : FunSpec({
res shouldBeRight subjects
}

test("getSubject") {
test("happy path getSubject") {
// arrange
val mockSchema = mockk<SchemaRegistryClient> {
every { getAllVersions(any()) } returns listOf(1, 2, 3)
Expand All @@ -37,4 +37,16 @@ class SchemaRegistryTest : FunSpec({
// assert
res shouldBeRight { }
}

test("happy path deleteSubject") {
// arrange
val mockSchema = mockk<SchemaRegistryClient> {
every { deleteSubject(any()) } returns listOf(1)
}
val sut = SchemaRegistry(mockSchema)
// act
val res = sut.deleteSubject("subject1")
// assert
res shouldBeRight { }
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package insulator.viewmodel.configurations

import arrow.core.left
import insulator.lib.kafka.SchemaRegistry
import insulator.viewmodel.main.schemaregistry.ListSchemaViewModel
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import tornadofx.* // ktlint-disable no-wildcard-imports
import kotlin.reflect.KClass

class ListSchemaViewModelTest : FunSpec({

val errorMessage = "Example error"

test("Show an error if unable to retrieve the configuration") {
// arrange
val sut = ListSchemaViewModel()
// act
val clusters = sut.listSchema
// assert
clusters.size shouldBe 0
sut.error.value!!.message shouldBe errorMessage
}

beforeTest {
FX.dicontainer = object : DIContainer {
@Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY")
override fun <T : Any> getInstance(type: KClass<T>): T = when (type.java) {
SchemaRegistry::class.java -> mockk<SchemaRegistry> {
every { getAllSubjects() } returns Throwable(errorMessage).left()
}
else -> throw IllegalArgumentException("Missing dependency")
} as T
}
}
})

0 comments on commit c7e7c25

Please sign in to comment.