Skip to content

Commit

Permalink
Add index, fix links
Browse files Browse the repository at this point in the history
  • Loading branch information
adrw committed Jan 10, 2025
1 parent 726af0c commit e8512ea
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 56 deletions.
2 changes: 2 additions & 0 deletions service/src/main/kotlin/app/cash/backfila/ui/UiModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package app.cash.backfila.ui
import app.cash.backfila.ui.actions.BackfillShowButtonHandlerAction
import app.cash.backfila.ui.actions.ServiceAutocompleteAction
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,6 +12,7 @@ import misk.web.WebActionModule
class UiModule : KAbstractModule() {
override fun configure() {
// Pages
install(WebActionModule.create<IndexAction>())
install(WebActionModule.create<ServiceShowAction>())
install(WebActionModule.create<ServiceIndexAction>())
install(WebActionModule.create<BackfillShowAction>())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import misk.tailwind.Link
import misk.tailwind.pages.MenuSection
import misk.tailwind.pages.Navbar
import misk.web.HttpCall
import misk.web.ResponseBody
import misk.web.dashboard.DashboardHomeUrl
import misk.web.dashboard.DashboardNavbarItem
import misk.web.dashboard.DashboardTab
import misk.web.dashboard.HtmlLayout
import okio.BufferedSink
import wisp.deployment.Deployment

/**
Expand Down Expand Up @@ -113,6 +115,7 @@ class DashboardPageLayout @Inject constructor(
menuSections = buildMenuSections(
currentPath = path,
),
sortedMenuLinks = false,
) {
div("py-10") {
main {
Expand Down Expand Up @@ -142,55 +145,78 @@ class DashboardPageLayout @Inject constructor(
}
}

fun buildHtmlResponseBody(block: TagConsumer<*>.() -> Unit): ResponseBody = object : ResponseBody {
override fun writeTo(sink: BufferedSink) {
sink.writeUtf8(build(block))
}
}

private fun buildMenuSections(
currentPath: String,
): List<MenuSection> {
return transacter.transaction { session ->
val runningBackfills = queryFactory.newQuery<BackfillRunQuery>()
val runningBackfillsForCaller = queryFactory.newQuery<BackfillRunQuery>()
.createdByUser(callerProvider.get()!!.user!!)
.state(BackfillState.RUNNING)
.orderByUpdatedAtDesc()
.list(session)

// TODO get services from REgistry for user || group backfills by service
val services = runningBackfillsForCaller.groupBy { it.service }

listOf(
MenuSection(
title = "Backfila",
links = listOf(
Link(
label = "Home",
href = "/",
isSelected = currentPath == "/",
),
Link(
label = "Services",
href = "/services/",
isSelected = currentPath.startsWith("/services/"),
isSelected = currentPath == "/services/",
),
Link(
label = "Backfills",
href = "/backfills/",
isSelected = currentPath.startsWith("/backfills/"),
isSelected = currentPath == "/backfills/",
),
),
),
MenuSection(
title = "Your Services",
links = listOf(
Link(
label = "Fine Dining",
href = "/services/?q=FineDining",
isSelected = currentPath.startsWith("/services/?q=FindDining"),
),
) + if (services.isNotEmpty()) {
listOf(
MenuSection(
title = "Your Services",
links = services.map { (service, backfills) ->
val variant = if (service.variant == "default") "" else service.variant
Link(
label = service.registry_name,
href = "/services/${service.registry_name}/$variant",
isSelected = currentPath.startsWith("/services/${service.registry_name}/$variant"),
)
},
),
),
MenuSection(
title = "Your Backfills",
links = runningBackfills.map { backfill ->
Link(
label = backfill.service.registry_name + " #" + backfill.id,
href = "/backfills/${backfill.id}",
isSelected = currentPath.startsWith("/backfills/${backfill.id}"),
)
},
),
)
)
} else {
listOf()
} + if (runningBackfillsForCaller.isNotEmpty()) {
listOf(
MenuSection(
title = "Your Backfills",
links = runningBackfillsForCaller.map { backfill ->
Link(
label = backfill.service.registry_name + " #" + backfill.id,
href = "/backfills/${backfill.id}",
isSelected = currentPath.startsWith("/backfills/${backfill.id}"),
)
},
),
)
} else {
listOf()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import app.cash.backfila.service.persistence.BackfillState
import app.cash.backfila.ui.actions.BackfillShowButtonHandlerAction
import app.cash.backfila.ui.components.AlertError
import app.cash.backfila.ui.components.AlertSupport
import app.cash.backfila.ui.components.DashboardLayout
import app.cash.backfila.ui.components.DashboardPageLayout
import app.cash.backfila.ui.components.PageTitle
import app.cash.backfila.ui.components.ProgressBar
import javax.inject.Inject
Expand All @@ -30,7 +30,6 @@ import kotlinx.html.td
import kotlinx.html.th
import kotlinx.html.thead
import kotlinx.html.tr
import misk.hotwire.buildHtmlResponseBody
import misk.security.authz.Authenticated
import misk.tailwind.Link
import misk.web.Get
Expand All @@ -45,6 +44,7 @@ import misk.web.mediatype.MediaTypes
class BackfillShowAction @Inject constructor(
private val config: BackfilaConfig,
private val getBackfillStatusAction: GetBackfillStatusAction,
private val dashboardPageLayout: DashboardPageLayout,
) : WebAction {
@Get(PATH)
@ResponseContentType(MediaTypes.TEXT_HTML)
Expand All @@ -54,25 +54,20 @@ class BackfillShowAction @Inject constructor(
): Response<ResponseBody> {
if (id.toLongOrNull() == null) {
return Response(
buildHtmlResponseBody {
DashboardLayout(
title = "Backfill $id | Backfila",
path = PATH,
) {
dashboardPageLayout.newBuilder()
.title("Backfill $id | Backfila")
.buildHtmlResponseBody {
PageTitle("Backfill", id)
AlertError("Invalid Backfill Id [id=$id], must be of type Long.")
AlertSupport(config.support_button_label, config.support_button_url)
}
},
},
)
}
val backfill = getBackfillStatusAction.status(id.toLong())

val htmlResponseBody = buildHtmlResponseBody {
DashboardLayout(
title = "Backfill $id | Backfila",
path = PATH,
) {
val htmlResponseBody = dashboardPageLayout.newBuilder()
.title("Backfill $id | Backfila")
.buildHtmlResponseBody {
PageTitle("Backfill", id)

// TODO add Header buttons / metrics
Expand Down Expand Up @@ -306,7 +301,6 @@ class BackfillShowAction @Inject constructor(

AlertSupport(config.support_button_label, config.support_button_url)
}
}

return Response(htmlResponseBody)
}
Expand Down
73 changes: 73 additions & 0 deletions service/src/main/kotlin/app/cash/backfila/ui/pages/IndexAction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package app.cash.backfila.ui.pages

import app.cash.backfila.service.BackfilaConfig
import app.cash.backfila.ui.actions.ServiceAutocompleteAction
import app.cash.backfila.ui.components.AlertSupport
import app.cash.backfila.ui.components.DashboardPageLayout
import javax.inject.Inject
import kotlinx.html.dd
import kotlinx.html.div
import kotlinx.html.dl
import kotlinx.html.dt
import kotlinx.html.h1
import kotlinx.html.h3
import kotlinx.html.span
import misk.MiskCaller
import misk.scope.ActionScoped
import misk.security.authz.Authenticated
import misk.web.Get
import misk.web.QueryParam
import misk.web.ResponseContentType
import misk.web.actions.WebAction
import misk.web.mediatype.MediaTypes

class IndexAction @Inject constructor(
private val config: BackfilaConfig,
private val serviceAutocompleteAction: ServiceAutocompleteAction,
private val dashboardPageLayout: DashboardPageLayout,
private val callerProvider: ActionScoped<MiskCaller?>,
) : WebAction {
@Get(PATH)
@ResponseContentType(MediaTypes.TEXT_HTML)
@Authenticated(capabilities = ["users"])
fun get(
@QueryParam sc: String?,
): String = dashboardPageLayout
.newBuilder()
.title("Backfila Home")
.build {
h1("text-2xl") {
+"""Welcome to Backfila, """
span("font-bold font-mono") { +"""${callerProvider.get()?.user}""" }
+"""!"""
}

// Stats

div("py-10") {
h3("text-base font-semibold text-gray-900") { +"""Last 30 days""" }
dl("mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3") {
div("overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6") {
this@dl.dt("truncate text-sm font-medium text-gray-500") { +"""Total Records""" }
this@dl.dd("mt-1 text-3xl font-semibold tracking-tight text-gray-900") { +"""1,271,897""" }
}
div("overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6") {
this@dl.dt("truncate text-sm font-medium text-gray-500") { +"""Avg. Progress Rate""" }
this@dl.dd("mt-1 text-3xl font-semibold tracking-tight text-gray-900") { +"""58.16%""" }
}
div("overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6") {
this@dl.dt("truncate text-sm font-medium text-gray-500") { +"""Avg. Records Per Second""" }
this@dl.dd("mt-1 text-3xl font-semibold tracking-tight text-gray-900") { +"""2457""" }
}
}
}

// Running Backfills

AlertSupport(config.support_button_label, config.support_button_url)
}

companion object {
const val PATH = "/"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,6 @@ class ServiceIndexAction @Inject constructor(
}

companion object {
const val PATH = "/"
const val PATH = "/services/"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import app.cash.backfila.dashboard.GetBackfillRunsAction
import app.cash.backfila.service.BackfilaConfig
import app.cash.backfila.ui.components.AlertSupport
import app.cash.backfila.ui.components.BackfillsTable
import app.cash.backfila.ui.components.DashboardLayout
import app.cash.backfila.ui.components.DashboardPageLayout
import app.cash.backfila.ui.components.PageTitle
import java.net.HttpURLConnection
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.html.role
import kotlinx.html.ul
import misk.hotwire.buildHtmlResponseBody
import misk.security.authz.Authenticated
import misk.web.Get
import misk.web.PathParam
import misk.web.QueryParam
import misk.web.Response
import misk.web.ResponseBody
Expand All @@ -27,34 +27,32 @@ import okhttp3.Headers
class ServiceShowAction @Inject constructor(
private val config: BackfilaConfig,
private val getBackfillRunsAction: GetBackfillRunsAction,
private val dashboardPageLayout: DashboardPageLayout,
) : WebAction {
@Get(PATH)
@ResponseContentType(MediaTypes.TEXT_HTML)
@Authenticated(capabilities = ["users"])
fun get(
@QueryParam s: String?,
@PathParam service: String?,
@PathParam variantOrBlank: String = "",
@QueryParam("experimental") experimental: Boolean? = false,
): Response<ResponseBody> {
if (s.isNullOrBlank()) {
if (service.isNullOrBlank()) {
return Response(
body = "go to /".toResponseBody(),
statusCode = HttpURLConnection.HTTP_MOVED_TEMP,
headers = Headers.headersOf("Location", "/"),
)
}
val variant = variantOrBlank.ifBlank { "default" }

val serviceName = s.split("/").first()
val variant = s.split("/").last()
val backfillRuns = getBackfillRunsAction.backfillRuns(service, variant)

val backfillRuns = getBackfillRunsAction.backfillRuns(serviceName, variant)

val htmlResponseBody = buildHtmlResponseBody {
// TODO show default if other variants and probably link to a switcher
val label = if (variant == "default") serviceName else "$serviceName ($variant)"
DashboardLayout(
title = "$label | Backfila",
path = PATH,
) {
// TODO show default if other variants and probably link to a switcher
val label = if (variant == "default") service else "$service ($variant)"
val htmlResponseBody = dashboardPageLayout.newBuilder()
.title("$label | Backfila")
.buildHtmlResponseBody {
PageTitle("Service", label)

// TODO Add completed table
Expand All @@ -68,12 +66,11 @@ class ServiceShowAction @Inject constructor(

AlertSupport(config.support_button_label, config.support_button_url)
}
}

return Response(htmlResponseBody)
}

companion object {
const val PATH = "/services/"
const val PATH = "/services/{service}/{variantOrBlank}"
}
}

0 comments on commit e8512ea

Please sign in to comment.