From 9cc5deeabe390857381e7519151872ef30f4674a Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Thu, 16 Apr 2026 01:10:10 +0300 Subject: [PATCH] fix(dashboard): fall back to .firstTimestamp in Event Time column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit core/v1 Events populate .lastTimestamp and .firstTimestamp but leave .eventTime null; events.k8s.io/v1 Events do the opposite. The previous column bound to .eventTime alone and rendered 'Invalid Date' for every Helm-generated event. Extend createTimestampColumn with an optional second jsonPath that is encoded as a nested reqsJsonPath fallback in the template, and use .eventTime → .firstTimestamp for Event Time and .lastTimestamp → .eventTime for Last Seen so both APIs render correctly. Signed-off-by: Aleksei Sviridkin --- .../controller/dashboard/static_helpers.go | 27 ++++++++++++++++--- .../controller/dashboard/static_refactored.go | 9 ++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/internal/controller/dashboard/static_helpers.go b/internal/controller/dashboard/static_helpers.go index 1a1018cb..db37e05b 100644 --- a/internal/controller/dashboard/static_helpers.go +++ b/internal/controller/dashboard/static_helpers.go @@ -650,12 +650,31 @@ func createStringColumn(name, jsonPath string) map[string]any { } } -// createTimestampColumn creates a timestamp column with custom formatting -func createTimestampColumn(name, jsonPath string) map[string]any { +// createTimestampColumn creates a timestamp column with custom formatting. +// Extra jsonPaths act as ordered fallbacks: the first non-null path wins, +// and `-` is used only when every path is null. Depth is capped at two +// because the template parser's non-greedy matcher cannot track more than +// one level of alternating quotes in a nested reqsJsonPath fallback. +func createTimestampColumn(name string, jsonPaths ...string) map[string]any { + if len(jsonPaths) == 0 { + panic("createTimestampColumn requires at least one jsonPath") + } + if len(jsonPaths) > 2 { + panic("createTimestampColumn supports at most two jsonPaths (primary + one fallback)") + } + + var text string + switch len(jsonPaths) { + case 1: + text = "{reqsJsonPath[0]['" + jsonPaths[0] + "']['-']}" + case 2: + text = "{reqsJsonPath[0]['" + jsonPaths[0] + "'][\"{reqsJsonPath[0]['" + jsonPaths[1] + "']['-']}\"]}" + } + return map[string]any{ "name": name, "type": "factory", - "jsonPath": jsonPath, + "jsonPath": jsonPaths[0], "customProps": map[string]any{ "disableEventBubbling": true, "items": []any{ @@ -673,7 +692,7 @@ func createTimestampColumn(name, jsonPath string) map[string]any { "data": map[string]any{ "formatter": "timestamp", "id": "time-value", - "text": "{reqsJsonPath[0]['" + jsonPath + "']['-']}", + "text": text, }, }, }, diff --git a/internal/controller/dashboard/static_refactored.go b/internal/controller/dashboard/static_refactored.go index a73026bc..d96bbaaa 100644 --- a/internal/controller/dashboard/static_refactored.go +++ b/internal/controller/dashboard/static_refactored.go @@ -224,10 +224,13 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid createStringColumn("Name", ".name"), }), - // Factory details events + // Factory details events. Event Time falls back to `.firstTimestamp` + // because core/v1 Events (Helm, controller-runtime) populate only the + // legacy timestamps while events.k8s.io/v1 Events populate only + // `.eventTime`; taking the first non-null of the two covers both. createCustomColumnsOverride("factory-details-events", []any{ - createTimestampColumn("Last Seen", ".lastTimestamp"), - createTimestampColumn("Event Time", ".eventTime"), + createTimestampColumn("Last Seen", ".lastTimestamp", ".eventTime"), + createTimestampColumn("Event Time", ".eventTime", ".firstTimestamp"), createStringColumn("Type", ".type"), createStringColumn("Reason", ".reason"), createStringColumn("Object", ".involvedObject.kind"),