Skip to content

Commit 5639a74

Browse files
authored
Make menu links configurable (#434)
Now internal deployments can add their own custom links which will be added to the menu. <img width="574" alt="Screenshot 2025-04-14 at 15 59 35" src="https://github.com/user-attachments/assets/3a833224-a085-4314-86e4-434356abafd3" />
1 parent 845f49b commit 5639a74

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

service/src/main/kotlin/app/cash/backfila/ui/UiModule.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,10 @@ class UiModule : KAbstractModule() {
3030
install(WebActionModule.create<BackfillShowButtonHandlerAction>())
3131
}
3232
}
33+
34+
/**
35+
* Identifies the Misk dashboard for Backfila so dashboard links and other customization can be added.
36+
*/
37+
@Retention(AnnotationRetention.RUNTIME)
38+
@Target(AnnotationTarget.FUNCTION)
39+
annotation class BackfilaDashboard

service/src/main/kotlin/app/cash/backfila/ui/components/DashboardPageLayout.kt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import app.cash.backfila.service.BackfilaConfig
55
import app.cash.backfila.service.persistence.BackfilaDb
66
import app.cash.backfila.service.persistence.BackfillRunQuery
77
import app.cash.backfila.service.persistence.BackfillState
8+
import app.cash.backfila.ui.BackfilaDashboard
89
import app.cash.backfila.ui.pages.IndexAction
910
import jakarta.inject.Inject
1011
import kotlinx.html.TagConsumer
@@ -30,6 +31,7 @@ import misk.tailwind.pages.MenuSection
3031
import misk.tailwind.pages.Navbar
3132
import misk.web.HttpCall
3233
import misk.web.ResponseBody
34+
import misk.web.dashboard.DashboardTab
3335
import misk.web.dashboard.HtmlLayout
3436
import okio.BufferedSink
3537
import wisp.deployment.Deployment
@@ -47,6 +49,7 @@ class DashboardPageLayout @Inject constructor(
4749
private val getBackfillRunsAction: GetBackfillRunsAction,
4850
@BackfilaDb private val transacter: Transacter,
4951
private val queryFactory: Query.Factory,
52+
private val allTabs: List<DashboardTab>,
5053
) {
5154
private var newBuilder = false
5255
private var headBlock: TagConsumer<*>.() -> Unit = {}
@@ -57,6 +60,10 @@ class DashboardPageLayout @Inject constructor(
5760
clientHttpCall.get().url.encodedPath
5861
}
5962

63+
private val backfilaLinks by lazy {
64+
allTabs.filter { it.dashboardAnnotationKClass == BackfilaDashboard::class }
65+
}
66+
6067
private fun setNewBuilder() = apply { newBuilder = true }
6168

6269
fun newBuilder(): DashboardPageLayout = DashboardPageLayout(
@@ -67,6 +74,7 @@ class DashboardPageLayout @Inject constructor(
6774
getBackfillRunsAction = getBackfillRunsAction,
6875
transacter = transacter,
6976
queryFactory = queryFactory,
77+
allTabs = allTabs,
7078
).setNewBuilder()
7179

7280
fun title(title: String) = apply {
@@ -181,9 +189,30 @@ class DashboardPageLayout @Inject constructor(
181189
href = "/backfills/",
182190
isSelected = currentPath == "/backfills/",
183191
),
184-
),
192+
) + backfilaLinks.filter { it.menuCategory == "Backfila" }.map { tab ->
193+
Link(
194+
label = tab.menuLabel,
195+
href = tab.menuUrl,
196+
isSelected = currentPath.startsWith(tab.menuUrl),
197+
)
198+
},
185199
),
186-
) + if (services.isNotEmpty()) {
200+
) + if (backfilaLinks.filterNot { it.menuCategory == "Backfila" }.isNotEmpty()) {
201+
backfilaLinks.filterNot { it.menuCategory == "Backfila" }.groupBy { it.menuCategory }.map { (category, tabs) ->
202+
MenuSection(
203+
title = category,
204+
links = tabs.map { tab ->
205+
Link(
206+
label = tab.menuLabel,
207+
href = tab.menuUrl,
208+
isSelected = currentPath.startsWith(tab.menuUrl),
209+
)
210+
},
211+
)
212+
}
213+
} else {
214+
listOf()
215+
} + if (services.isNotEmpty()) {
187216
listOf(
188217
MenuSection(
189218
title = "Your Services",

service/src/test/kotlin/app/cash/backfila/development/BackfilaDevelopmentService.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import app.cash.backfila.protos.clientservice.RunBatchResponse
1616
import app.cash.backfila.service.BackfilaConfig
1717
import app.cash.backfila.service.BackfilaServiceModule
1818
import app.cash.backfila.service.persistence.DbBackfillRun
19+
import app.cash.backfila.ui.BackfilaDashboard
1920
import misk.MiskApplication
2021
import misk.MiskRealServiceModule
2122
import misk.audit.NoOpAuditClientModule
@@ -28,8 +29,10 @@ import misk.jdbc.DataSourceConfig
2829
import misk.jdbc.DataSourceType
2930
import misk.security.authz.FakeCallerAuthenticator
3031
import misk.security.authz.MiskCallerAuthenticator
32+
import misk.security.authz.Unauthenticated
3133
import misk.web.MiskWebModule
3234
import misk.web.WebConfig
35+
import misk.web.dashboard.DashboardModule
3336
import okio.ByteString.Companion.encodeUtf8
3437
import wisp.deployment.Deployment
3538

@@ -54,6 +57,22 @@ fun main(args: Array<String>) {
5457
multibind<MiskCallerAuthenticator>().to<FakeCallerAuthenticator>()
5558
bind<ViewLogsUrlProvider>().to<DevelopmentViewLogsUrlProvider>()
5659

60+
// Example custom link that shows up in navbar
61+
install(
62+
DashboardModule.createMenuLink<BackfilaDashboard, Unauthenticated>(
63+
label = "Go to Repo",
64+
url = "https://github.com/cashapp/backfila",
65+
category = "Backfila",
66+
),
67+
)
68+
install(
69+
DashboardModule.createMenuLink<BackfilaDashboard, Unauthenticated>(
70+
label = "Test other link",
71+
url = "https://github.com/cashapp/backfila",
72+
category = "Alpha",
73+
),
74+
)
75+
5776
newMapBinder<String, BackfilaCallbackConnectorProvider>(ForConnectors::class)
5877
.permitDuplicates().addBinding("DEV")
5978
.toInstance(object : BackfilaCallbackConnectorProvider {

0 commit comments

Comments
 (0)