Skip to content

Commit 0cbe75e

Browse files
emilydespasclaude
andauthored
Add ViewDashboard action for external dashboard integration (#537)
## What Adds a "View Dashboard" button to the backfill run page that redirects to external dashboard providers. Follows the same provider pattern as the existing ViewLogsAction. ## Why Enables integrators to link backfill runs to external monitoring/observability dashboards, providing a consistent way to view runtime metrics and health data alongside logs. Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 1b18788 commit 0cbe75e

File tree

5 files changed

+90
-0
lines changed

5 files changed

+90
-0
lines changed

service-self-backfill/src/test/kotlin/app/cash/backfila/service/selfbackfill/BackfilaSelfBackfillDevelopmentService.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import app.cash.backfila.client.BackfilaCallbackConnector
44
import app.cash.backfila.client.BackfilaCallbackConnectorProvider
55
import app.cash.backfila.client.BackfilaDefaultEndpointConfigModule
66
import app.cash.backfila.client.ForConnectors
7+
import app.cash.backfila.dashboard.ViewDashboardUrlProvider
78
import app.cash.backfila.dashboard.ViewLogsUrlProvider
89
import app.cash.backfila.protos.clientservice.GetNextBatchRangeRequest
910
import app.cash.backfila.protos.clientservice.GetNextBatchRangeResponse
@@ -52,6 +53,7 @@ internal fun main(args: Array<String>) {
5253
bind<MiskCaller>().annotatedWith<DevelopmentOnly>()
5354
.toInstance(MiskCaller(user = "testfila"))
5455
bind<ViewLogsUrlProvider>().to<DevelopmentViewLogsUrlProvider>()
56+
bind<ViewDashboardUrlProvider>().to<DevelopmentViewDashboardUrlProvider>()
5557

5658
newMapBinder<String, BackfilaCallbackConnectorProvider>(ForConnectors::class)
5759
.permitDuplicates().addBinding("DEV")
@@ -130,3 +132,9 @@ internal class DevelopmentViewLogsUrlProvider : ViewLogsUrlProvider {
130132
return "/"
131133
}
132134
}
135+
136+
internal class DevelopmentViewDashboardUrlProvider : ViewDashboardUrlProvider {
137+
override fun getUrl(session: Session, backfillRun: DbBackfillRun): String {
138+
return "/"
139+
}
140+
}

service/src/main/kotlin/app/cash/backfila/dashboard/BackfilaWebActionsModule.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class BackfilaWebActionsModule() : KAbstractModule() {
1818
install(WebActionModule.create<GetBackfillStatusAction>())
1919
install(WebActionModule.create<UpdateBackfillAction>())
2020
install(WebActionModule.create<ViewLogsAction>())
21+
install(WebActionModule.create<ViewDashboardAction>())
2122
install(WebActionModule.create<EditPartitionCursorAction>())
2223
install(WebActionModule.create<EditPartitionCursorHandlerAction>())
2324
install(WebActionModule.create<SearchBackfillNamesAction>())
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package app.cash.backfila.dashboard
2+
3+
import app.cash.backfila.service.persistence.BackfilaDb
4+
import app.cash.backfila.service.persistence.DbBackfillRun
5+
import java.net.HttpURLConnection
6+
import javax.inject.Inject
7+
import misk.exceptions.BadRequestException
8+
import misk.hibernate.Id
9+
import misk.hibernate.Session
10+
import misk.hibernate.Transacter
11+
import misk.hibernate.loadOrNull
12+
import misk.security.authz.Authenticated
13+
import misk.web.Get
14+
import misk.web.PathParam
15+
import misk.web.Response
16+
import misk.web.ResponseBody
17+
import misk.web.actions.WebAction
18+
import misk.web.toResponseBody
19+
import okhttp3.Headers
20+
21+
interface ViewDashboardUrlProvider {
22+
fun getUrl(session: Session, backfillRun: DbBackfillRun): String
23+
}
24+
25+
class ViewDashboardAction @Inject constructor(
26+
@BackfilaDb private val transacter: Transacter,
27+
private val viewDashboardUrlProvider: ViewDashboardUrlProvider,
28+
) : WebAction {
29+
@Get("/backfills/{id}/view-dashboard")
30+
@Authenticated(allowAnyUser = true)
31+
fun viewDashboard(
32+
@PathParam id: Long,
33+
): Response<ResponseBody> {
34+
val url = getUrl(id)
35+
return Response(
36+
body = "go to $url".toResponseBody(),
37+
statusCode = HttpURLConnection.HTTP_MOVED_TEMP,
38+
headers = Headers.headersOf("Location", url),
39+
)
40+
}
41+
42+
fun getUrl(id: Long) = transacter.transaction { session ->
43+
val backfillRun = session.loadOrNull<DbBackfillRun>(Id(id))
44+
?: throw BadRequestException("backfill $id doesn't exist")
45+
viewDashboardUrlProvider.getUrl(session, backfillRun)
46+
}
47+
}

service/src/main/kotlin/app/cash/backfila/ui/pages/BackfillShowAction.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package app.cash.backfila.ui.pages
33
import app.cash.backfila.dashboard.EditPartitionCursorAction
44
import app.cash.backfila.dashboard.GetBackfillStatusAction
55
import app.cash.backfila.dashboard.GetBackfillStatusResponse
6+
import app.cash.backfila.dashboard.ViewDashboardAction
67
import app.cash.backfila.dashboard.ViewLogsAction
78
import app.cash.backfila.service.persistence.BackfillState
89
import app.cash.backfila.ui.actions.BackfillShowButtonHandlerAction
@@ -59,6 +60,7 @@ class BackfillShowAction @Inject constructor(
5960
private val getBackfillStatusAction: GetBackfillStatusAction,
6061
private val dashboardPageLayout: DashboardPageLayout,
6162
private val viewLogsAction: ViewLogsAction,
63+
private val viewDashboardAction: ViewDashboardAction,
6264
private val backfillShowButtonHandlerAction: BackfillShowButtonHandlerAction,
6365
private val httpCall: ActionScoped<HttpCall>,
6466
) : WebAction {
@@ -514,6 +516,14 @@ class BackfillShowAction @Inject constructor(
514516
href = viewLogsAction.getUrl(id),
515517
),
516518
),
519+
DescriptionListRow(
520+
label = "Dashboard",
521+
description = "",
522+
button = Link(
523+
label = VIEW_DASHBOARD_BUTTON_LABEL,
524+
href = viewDashboardAction.getUrl(id),
525+
),
526+
),
517527
) + if (parameters?.isNotEmpty() == true) {
518528
listOf(
519529
DescriptionListRow(
@@ -628,6 +638,21 @@ class BackfillShowAction @Inject constructor(
628638
href = button.href
629639
target = "_blank"
630640

641+
button(
642+
classes = "rounded-md font-medium text-indigo-600 hover:text-indigo-500",
643+
) {
644+
type = ButtonType.submit
645+
+button.label
646+
}
647+
}
648+
}
649+
} else if (button.label == VIEW_DASHBOARD_BUTTON_LABEL) {
650+
span("ml-4 flex-shrink-0") {
651+
// View dashboard button will link to external dashboard provider
652+
a {
653+
href = button.href
654+
target = "_blank"
655+
631656
button(
632657
classes = "rounded-md font-medium text-indigo-600 hover:text-indigo-500",
633658
) {
@@ -836,5 +861,6 @@ class BackfillShowAction @Inject constructor(
836861
const val DELETE_STATE_BUTTON_LABEL = "Delete"
837862
const val UPDATE_BUTTON_LABEL = "Update"
838863
const val VIEW_LOGS_BUTTON_LABEL = "View Logs"
864+
const val VIEW_DASHBOARD_BUTTON_LABEL = "View Dashboard"
839865
}
840866
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import app.cash.backfila.client.BackfilaCallbackConnector
44
import app.cash.backfila.client.BackfilaCallbackConnectorProvider
55
import app.cash.backfila.client.BackfilaDefaultEndpointConfigModule
66
import app.cash.backfila.client.ForConnectors
7+
import app.cash.backfila.dashboard.ViewDashboardUrlProvider
78
import app.cash.backfila.dashboard.ViewLogsUrlProvider
89
import app.cash.backfila.development.DevServiceConstants.Companion.BACKFILA_PORT
910
import app.cash.backfila.protos.clientservice.GetNextBatchRangeRequest
@@ -56,6 +57,7 @@ fun main(args: Array<String>) {
5657
install(MiskWebModule(webConfig))
5758
multibind<MiskCallerAuthenticator>().to<FakeCallerAuthenticator>()
5859
bind<ViewLogsUrlProvider>().to<DevelopmentViewLogsUrlProvider>()
60+
bind<ViewDashboardUrlProvider>().to<DevelopmentViewDashboardUrlProvider>()
5961

6062
// Example custom link that shows up in navbar
6163
install(
@@ -154,6 +156,12 @@ internal class DevelopmentViewLogsUrlProvider : ViewLogsUrlProvider {
154156
}
155157
}
156158

159+
internal class DevelopmentViewDashboardUrlProvider : ViewDashboardUrlProvider {
160+
override fun getUrl(session: Session, backfillRun: DbBackfillRun): String {
161+
return "/${backfillRun.service.registry_name}/${backfillRun.service.variant}/${backfillRun.id}/dashboard"
162+
}
163+
}
164+
157165
class DevServiceConstants {
158166
companion object {
159167
const val BACKFILA_PORT = 8080

0 commit comments

Comments
 (0)