Skip to content

Commit

Permalink
UI Rewrite (#407)
Browse files Browse the repository at this point in the history
https://github.com/user-attachments/assets/10bf8ccb-2109-4ec5-bf05-60d2b9a08a46

# Coming in follow up PRs

- [ ] Fix auto-reload using turbo frame src instead of full page reload
- [ ] Add service filter for only services that have running backfills
- [ ] Add button to stop all backfills
- [ ] Show custom parameters on backfill show screen
- [ ] Make clone/create screen more dense, everything above the fold
- [ ] Make backfill show table no scrolling (full width configuration,
then partitions, then logs)
- [ ] Upstream common elements to Misk
  • Loading branch information
adrw authored Jan 23, 2025
1 parent b0fbc31 commit 596558a
Show file tree
Hide file tree
Showing 34 changed files with 1,734 additions and 799 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
jooq = "3.18.1"
ktlint = "0.47.1"
kotlin = "1.9.23"
misk = "2024.06.26.052833-88986ac"
misk = "2025.01.09.184726-680bca2"
okhttp = "5.0.0-alpha.14"
wire = "5.0.0"
sqldelight = "2.0.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ interface BackfillRunQuery : Query<DbBackfillRun> {
@Constraint("state", Operator.NE)
fun stateNot(state: BackfillState): BackfillRunQuery

@Constraint("created_by_user", Operator.EQ)
fun createdByUser(user: String): BackfillRunQuery

@Order("id", asc = false)
fun orderByIdDesc(): BackfillRunQuery

@Order("updated_at", asc = false)
fun orderByUpdatedAtDesc(): BackfillRunQuery
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.cash.backfila.service.persistence

import misk.hibernate.Constraint
import misk.hibernate.Id
import misk.hibernate.Order
import misk.hibernate.Query

Expand All @@ -16,4 +17,7 @@ interface ServiceQuery : Query<DbService> {

@Order("variant")
fun orderByVariant(): ServiceQuery

@Constraint("id")
fun id(id: Id<DbService>): ServiceQuery
}
8 changes: 8 additions & 0 deletions service/src/main/kotlin/app/cash/backfila/ui/DashboardUrls.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package app.cash.backfila.ui

object DashboardUrls {
fun app(appName: String) = "/app/$appName"
fun createDeploy(appName: String) = "/app/$appName/deploy"
fun deploy(appName: String, deployName: String) = "/app/$appName/deploy/$deployName"
fun setMinimalCommitTimestamp(appName: String) = "/app/$appName/set-minimal-commit-timestamp"
}
69 changes: 0 additions & 69 deletions service/src/main/kotlin/app/cash/backfila/ui/PathBuilder.kt

This file was deleted.

16 changes: 13 additions & 3 deletions service/src/main/kotlin/app/cash/backfila/ui/UiModule.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package app.cash.backfila.ui

import app.cash.backfila.ui.actions.BackfillCreateHandlerAction
import app.cash.backfila.ui.actions.BackfillShowButtonHandlerAction
import app.cash.backfila.ui.actions.ServiceAutocompleteAction
import app.cash.backfila.ui.pages.BackfillCreateAction
import app.cash.backfila.ui.pages.BackfillCreateIndexAction
import app.cash.backfila.ui.pages.BackfillCreateServiceIndexAction
import app.cash.backfila.ui.pages.BackfillIndexAction
import app.cash.backfila.ui.pages.BackfillShowAction
import app.cash.backfila.ui.pages.IndexAction
import app.cash.backfila.ui.pages.ServiceIndexAction
import app.cash.backfila.ui.pages.ServiceShowAction
import misk.inject.KAbstractModule
Expand All @@ -11,12 +16,17 @@ import misk.web.WebActionModule
class UiModule : KAbstractModule() {
override fun configure() {
// Pages
install(WebActionModule.create<ServiceShowAction>())
install(WebActionModule.create<IndexAction>())
install(WebActionModule.create<ServiceIndexAction>())
install(WebActionModule.create<ServiceShowAction>())
install(WebActionModule.create<BackfillCreateIndexAction>())
install(WebActionModule.create<BackfillCreateServiceIndexAction>())
install(WebActionModule.create<BackfillCreateAction>())
install(WebActionModule.create<BackfillIndexAction>())
install(WebActionModule.create<BackfillShowAction>())

// Other
install(WebActionModule.create<BackfillCreateHandlerAction>())
install(WebActionModule.create<BackfillShowButtonHandlerAction>())
install(WebActionModule.create<ServiceAutocompleteAction>())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package app.cash.backfila.ui.actions

import app.cash.backfila.dashboard.CreateBackfillAction
import app.cash.backfila.protos.service.CreateBackfillRequest
import app.cash.backfila.ui.pages.BackfillCreateAction.BackfillCreateField
import javax.inject.Inject
import javax.inject.Singleton
import misk.scope.ActionScoped
import misk.security.authz.Authenticated
import misk.web.Get
import misk.web.HttpCall
import misk.web.Response
import misk.web.ResponseBody
import misk.web.ResponseContentType
import misk.web.actions.WebAction
import misk.web.mediatype.MediaTypes
import misk.web.toResponseBody
import okhttp3.Headers
import okio.ByteString.Companion.encodeUtf8

@Singleton
class BackfillCreateHandlerAction @Inject constructor(
private val createBackfillAction: CreateBackfillAction,
private val httpCall: ActionScoped<HttpCall>,
) : WebAction {
@Get(PATH)
@ResponseContentType(MediaTypes.TEXT_HTML)
@Authenticated(capabilities = ["users"])
fun get(): Response<ResponseBody> {
// Parse form
val formFieldNames = this.httpCall.get().asOkHttpRequest().url.queryParameterNames
val formFields = formFieldNames.associateWith { this.httpCall.get().asOkHttpRequest().url.queryParameter(it) }

// Submit create call
val createRequestBuilder = CreateBackfillRequest.Builder()
formFields[BackfillCreateField.BACKFILL_NAME.fieldId]?.ifNotBlank { createRequestBuilder.backfill_name(it) }
createRequestBuilder.dry_run(
when (formFields[BackfillCreateField.DRY_RUN.fieldId]) {
// Unchecked box in UI will not send a value
"off", null -> false
else -> true
},
)
formFields[BackfillCreateField.RANGE_START.fieldId]?.ifNotBlank { createRequestBuilder.pkey_range_start(it.encodeUtf8()) }
formFields[BackfillCreateField.RANGE_END.fieldId]?.ifNotBlank { createRequestBuilder.pkey_range_end(it.encodeUtf8()) }
formFields[BackfillCreateField.BATCH_SIZE.fieldId]?.ifNotBlank { createRequestBuilder.batch_size(it.toLongOrNull()) }
formFields[BackfillCreateField.SCAN_SIZE.fieldId]?.ifNotBlank { createRequestBuilder.scan_size(it.toLongOrNull()) }
formFields[BackfillCreateField.THREADS_PER_PARTITION.fieldId]?.ifNotBlank { createRequestBuilder.num_threads(it.toIntOrNull()) }
formFields[BackfillCreateField.EXTRA_SLEEP_MS.fieldId]?.ifNotBlank { createRequestBuilder.extra_sleep_ms(it.toLongOrNull()) }
formFields[BackfillCreateField.BACKOFF_SCHEDULE.fieldId]?.ifNotBlank { createRequestBuilder.backoff_schedule(it) }
val customParameters = formFields.filter { it.key.startsWith(BackfillCreateField.CUSTOM_PARAMETER_PREFIX.fieldId) }
.mapValues { it.value?.encodeUtf8() }
if (customParameters.isNotEmpty()) {
createRequestBuilder.parameter_map(customParameters)
}

val response = createBackfillAction.create(
service = formFields[BackfillCreateField.SERVICE.fieldId]!!,
variant = formFields[BackfillCreateField.VARIANT.fieldId]!!,
request = createRequestBuilder.build(),
)

val id = response.backfill_run_id

return Response(
body = "go to /backfills/$id".toResponseBody(),
statusCode = 303,
headers = Headers.headersOf("Location", "/backfills/$id"),
)
}

companion object {
const val PATH = "/api/backfill/create"

private fun <T>String?.ifNotBlank(block: (String) -> T) = if (this.isNullOrBlank()) null else block(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ class BackfillShowButtonHandlerAction @Inject constructor(
"backoff_schedule" -> {
updateBackfillAction.update(id.toLong(), UpdateBackfillRequest(backoff_schedule = field_value))
}

// TODO add support for updating other fields, not just state
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app.cash.backfila.ui.actions

import app.cash.backfila.dashboard.GetServicesAction
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ServiceDataHelper @Inject constructor(
private val getServicesAction: GetServicesAction,
) {
fun getFlattenedServices(): Map<String, GetServicesAction.UiService> {
val services = getServicesAction.services().services
return services.flatMap { service ->
service.variants.map { variant -> "${service.name}/$variant" to service }
}.toMap()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app.cash.backfila.ui.components

import kotlinx.html.TagConsumer
import kotlinx.html.div

// TODO
// Currently this reloads the whole page every 10s
// We want to ideally only update within a turbo frame so the rest of the UI is stable
// (ie. update form for backfill show page)
fun TagConsumer<*>.AutoReload(block: TagConsumer<*>.() -> Unit) {
div {
attributes["data-controller"] = "auto-reload"
attributes["data-auto-reload-target"] = "frame"

block()
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package app.cash.backfila.ui.components

import app.cash.backfila.dashboard.UiBackfillRun
import app.cash.backfila.ui.PathBuilder
import app.cash.backfila.ui.pages.BackfillShowAction
import kotlinx.html.TagConsumer
import kotlinx.html.a
Expand All @@ -21,8 +20,6 @@ fun TagConsumer<*>.BackfillsTable(running: Boolean, backfills: List<UiBackfillRu
div("sm:flex sm:items-center") {
div("sm:flex-auto") {
h1("text-base font-semibold leading-6 text-gray-900") { +"""Backfills ($title)""" }
// TODO delete
// p("mt-2 text-sm text-gray-700") { +"""A list of all the users in your account including their name, title, email and role.""" }
}
}
div("mt-8 flow-root") {
Expand All @@ -48,7 +45,7 @@ fun TagConsumer<*>.BackfillsTable(running: Boolean, backfills: List<UiBackfillRu
"whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0",
) {
a(classes = "text-green-500 hover:underline") {
href = PathBuilder(path = BackfillShowAction.PATH.replace("{id}", it.id)).build()
href = BackfillShowAction.PATH.replace("{id}", it.id)
+it.name
}
}
Expand All @@ -68,17 +65,6 @@ fun TagConsumer<*>.BackfillsTable(running: Boolean, backfills: List<UiBackfillRu
"whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0",
) { +"""$it""" }
}

// TODO delete
// td(
// "relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0"
// ) {
// a(classes = "text-indigo-600 hover:text-indigo-900") {
// href = "#"
// +"""Edit"""
// span("sr-only") { +""", Lindsay Walton""" }
// }
// }
}
}
}
Expand Down
Loading

0 comments on commit 596558a

Please sign in to comment.