Conversation
|
@rioloc: This pull request references OU-1040 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
Skipping CI for Draft Pull Request. |
|
@rioloc: This pull request references OU-1040 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
@rioloc: This pull request references OU-1040 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/test e2e-incidents |
|
/test e2e-incidents |
|
/label qe-approved |
|
@rioloc: This pull request references OU-1040 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
- add incidentsTimestamp in redux store - add two extra calls to retrieve min_over_time and last_over_time for incidents - enrich incident with absolute datapoints timestamps - update IncidentTooltip labels with absolute Start and End times
…entsTable on page refresh
- Fix initial state type mismatch for incidentsTimestamps and alertsTimestamps
(was [] instead of { minOverTime: [], lastOverTime: [] })
- Add defensive check in matchTimestampMetricForIncident for undefined timestamps
- Refactor incidents useEffect to fetch timestamps and incidents in parallel,
then use fetched values directly instead of stale closure values
- Refactor alerts useEffect with same pattern and add guards for empty
incidentForAlertProcessing and timeRanges
- Add timeRanges and rules to alerts useEffect dependency array
- Remove unused alertsTimestamps selector
Assisted-By: Claude Opus 4.5
14440d2 to
b8f4ed7
Compare
|
/test e2e-incidents |
|
/retest |
2 similar comments
|
/retest |
|
/retest |
|
/retest |
|
/hold |
|
/unhold |
|
/label qe-approved |
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: PeterYurkovich, rioloc The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
/hold |
|
New changes are detected. LGTM label has been removed. |
|
/retest |
3f05003 to
8b40410
Compare
8b40410 to
a5864d2
Compare
|
/test okd-scos-images |
|
linter error, please take a look |
|
PR needs rebase. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
@rioloc: This pull request references OU-1040 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
📝 WalkthroughWalkthroughThis pull request introduces timestamp-aware data enrichment for incidents and alerts. The changes add new API calls to fetch instantaneous timestamp metrics, compute Changes
Sequence Diagram(s)sequenceDiagram
participant IncidentsPage
participant API as API Layer
participant ProcessData as Process<br/>Incidents/Alerts
participant Store as State<br/>Store
participant Charts as Chart<br/>Components
IncidentsPage->>API: fetchInstantData(timestamps query)
IncidentsPage->>API: fetchDataForIncidentsAndAlerts(range query)
API-->>IncidentsPage: incidentsTimestamps
API-->>IncidentsPage: alertsTimestamps
API-->>IncidentsPage: prometheusResults
IncidentsPage->>Store: setIncidentsTimestamps(payload)
IncidentsPage->>Store: setAlertsTimestamps(payload)
Store-->>IncidentsPage: state updated
IncidentsPage->>ProcessData: processIncidentsForAlerts(incidentsTimestamps, results)
ProcessData->>ProcessData: matchTimestampMetricForIncident<br/>(compute firstTimestamp)
ProcessData-->>IncidentsPage: enrichedIncidents[firstTimestamp]
IncidentsPage->>ProcessData: convertToAlerts(results, incidents, alertsTimestamps)
ProcessData->>ProcessData: splitAbsoluteTimestampsByGap<br/>(handle gaps)
ProcessData->>ProcessData: assignAbsoluteFirstTimestamps<br/>(align intervals)
ProcessData-->>IncidentsPage: enrichedAlerts[firstTimestamp]
IncidentsPage->>Charts: render(enrichedIncidents, enrichedAlerts, incidentsTimestamps)
Charts->>Charts: createIncidentsChartBars(startDate from firstTimestamp)
Charts->>Charts: createAlertsChartBars(startDate from firstTimestamp)
Charts-->>IncidentsPage: rendered visualization
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
web/src/components/Incidents/IncidentsTable.tsx (1)
94-99:⚠️ Potential issue | 🟠 MajorMissing fallback when
firstTimestampis0.When no timestamp match is found,
firstTimestampdefaults to0, causinggetMinStartDateto return0— which renders as Jan 1, 1970 in the<Timestamp>component (Line 183) and breaks sort order.IncidentsDetailsRowTable.tsx(Lines 28-29) guards against this withfirstTimestamp > 0 ? firstTimestamp : alertsStartFiring, but this file lacks the same fallback.Proposed fix: add fallback to alertsStartFiring
return Math.min( - ...alert.alertsExpandedRowData.map((alertData) => alertData.firstTimestamp), + ...alert.alertsExpandedRowData.map((alertData) => + alertData.firstTimestamp > 0 ? alertData.firstTimestamp : alertData.alertsStartFiring, + ), );web/src/components/Incidents/IncidentsPage.tsx (1)
400-403:⚠️ Potential issue | 🟡 MinorInconsistent error logging:
console.loghere vsconsole.errorin the alerts fetch (line 313).The alerts-fetching
.catchon line 313 usesconsole.errorbut the incidents-fetching.catchhere still usesconsole.log.Proposed fix
.catch((err) => { // eslint-disable-next-line no-console - console.log(err); + console.error(err); });web/src/components/Incidents/utils.ts (1)
406-450:⚠️ Potential issue | 🟠 MajorUpdate
AlertsChartBartype definition to includestartDatefield, or remove it from the function if unused.The
createAlertsChartBarsfunction (line 430) addsstartDate: new Date(intervalFirstTimestamp * 1000)to each returned object, but theAlertsChartBartype definition inmodel.ts(lines 137-150) does not include this field. The function has an explicit return type annotation ofAlertsChartBar[], creating a type mismatch. Additionally,startDateis not accessed anywhere in the codebase, suggesting it may be dead code. Either addstartDate: Dateto theAlertsChartBartype or remove it from the function.
🤖 Fix all issues with AI agents
In `@web/src/components/Incidents/api.ts`:
- Around line 180-187: The code should call consoleFetchJSON(url) directly
(remove the redundant Promise.resolve) and defensively access the result to
avoid a TypeError: capture the response from consoleFetchJSON and use
response.data?.result || [] when constructing the returned PrometheusResponse
(keep the same shape with resultType: 'matrix'), ensuring you still return the
object as PrometheusResponse; reference the variables/functions response,
consoleFetchJSON and the PrometheusResponse return shape and mirror the optional
chaining used in fetchDataForIncidentsAndAlerts.
In `@web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx`:
- Around line 86-99: enrichedIncidentsData currently sets firstTimestamp to 0
when matchTimestampMetricForIncident returns undefined, causing "Jan 1, 1970" in
tooltips; change the fallback in the useMemo mapping so firstTimestamp uses the
incident's earliest value timestamp (e.g., the first element in incident.values)
when matchedMinTimestamp is undefined, parsing that timestamp with parseInt
(instead of '0'), and keep the rest of the mapping unchanged; this touches
enrichedIncidentsData, matchTimestampMetricForIncident usage, and will prevent
createIncidentsChartBars from receiving epoch-zero values for firstTimestamp.
In `@web/src/components/Incidents/processAlerts.spec.ts`:
- Around line 570-582: The test fixtures for AlertsTimestamps are using key
"value" (singular) but splitAbsoluteTimestampsByGap expects entries shaped like
production (entries have "values" as an array of [timestamp,string] pairs);
update the test objects in alertsTimestamps.minOverTime (and the three other
timestamp-matching tests) to use values: [[matchedMinTimestamp,
matchedMinTimestamp.toString()]] (i.e., change "value: [...]" to "values:
[[...]]") so they match the shape consumed by splitAbsoluteTimestampsByGap and
the AlertsTimestamps type.
In `@web/src/components/Incidents/processAlerts.ts`:
- Line 344: The code assumes incidents[0] exists when computing
incidentFirstTimestamp in processAlerts (after calling mergeIncidentsByKey),
which can throw if the merged incidents array is empty; update the public
function in processAlerts.ts to defensively handle an empty array by checking
incidents.length (or the result of mergeIncidentsByKey) before accessing
incidents[0].firstTimestamp and either return early/skip processing, or set
incidentFirstTimestamp to a safe default (e.g., undefined) and propagate that
state so callers won’t dereference a missing element; reference the variable
incidentFirstTimestamp and the mergeIncidentsByKey call to find where to add the
guard.
In `@web/src/components/Incidents/processIncidents.ts`:
- Around line 194-225: processIncidentsForAlerts currently falls back to '0'
when matchTimestampMetricForIncident returns no match, producing epoch
timestamps; change the fallback for firstTimestamp to use the incident's
earliest value timestamp (from incident.values) before finally defaulting to
'0'. Specifically, when computing firstTimestamp in processIncidentsForAlerts,
pick matchedMinTimestamp?.value?.[1] if present, otherwise use the timestamp
from incident.values (the earliest entry), and then parseInt that result to set
firstTimestamp; update any parseInt usage accordingly to avoid defaulting
straight to epoch.
In `@web/src/components/Incidents/utils.spec.ts`:
- Around line 156-179: The test setup and assertion for
matchTimestampMetricForIncident are placed directly in the describe block,
causing them to run during collection and orphaning subsequent it blocks; wrap
the variable declarations (incident, timestamps, result) and the
expect(result).toBe(...) assertion inside a new it('returns matching timestamp
metric', () => { ... }) block, and move the describe closing token (the final
"});") to after the existing it blocks so all it tests (including those on lines
181–270) are nested inside describe('matchTimestampMetricForIncident'); keep
references to matchTimestampMetricForIncident, incident, timestamps, and result
when relocating code.
In `@web/src/components/Incidents/utils.ts`:
- Around line 325-326: The code constructs startDate from
incident.firstTimestamp using new Date(incident.firstTimestamp * 1000) which
yields 1970-01-01 for a 0 timestamp; change the construction so that when
incident.firstTimestamp is missing/0 you set startDate to null (or undefined)
instead of a Date(0), update the startDate type to Date | null in the relevant
interface/shape (the startDate property in the object/type), and adjust
consumers (e.g., IncidentsChart.tsx and any rendering logic) to handle null
startDate appropriately; specifically locate the new Date(...) call and replace
it with a guarded expression that only creates a Date when
incident.firstTimestamp > 0 and otherwise assigns null, and update the type
annotations for startDate to match.
🧹 Nitpick comments (9)
web/src/components/Incidents/model.ts (1)
26-34: Consider replacingArray<any>with a proper type for Prometheus instant query results.Both
IncidentsTimestampsandAlertsTimestampsuseArray<any>forminOverTimeandlastOverTime. The data shape from Prometheus instant queries is well-known (an object withmetric: Record<string, string>andvalue: [number, string]). Typing these properly would catch misuse at compile time and improve discoverability.Additionally, the two types are structurally identical — a single shared type (or type alias) would reduce duplication.
Proposed typing improvement
+export type TimestampResult = { + metric: Record<string, string>; + value: [number, string]; +}; + -export type IncidentsTimestamps = { - minOverTime: Array<any>; - lastOverTime: Array<any>; -}; - -export type AlertsTimestamps = { - minOverTime: Array<any>; - lastOverTime: Array<any>; -}; +export type TimestampData = { + minOverTime: Array<TimestampResult>; + lastOverTime: Array<TimestampResult>; +}; + +export type IncidentsTimestamps = TimestampData; +export type AlertsTimestamps = TimestampData;web/src/components/Incidents/api.ts (1)
157-160:fetchparameter is accepted but unused.The
fetchparameter is never referenced in the function body —consoleFetchJSONis called directly on Line 180. This matches the pattern infetchDataForIncidentsAndAlerts(which also accepts but ignoresfetch), but it's misleading. Consider removing it or actually using it for consistency and testability.web/src/components/Incidents/processIncidents.spec.ts (1)
1037-1065: RedundantemptyTimestampsre-declarations in edge case tests.The
emptyTimestampsfixture at Lines 1039-1042 and 1055-1058 duplicates the one already declared in the parentdescribescope (Lines 672-675). The locally scoped copies can be removed since they're identical and the parent-scope variable is accessible.Proposed cleanup
describe('edge cases', () => { it('should handle empty array', () => { - const emptyTimestamps: IncidentsTimestamps = { - minOverTime: [], - lastOverTime: [], - }; const result = processIncidentsForAlerts([], emptyTimestamps); expect(result).toEqual([]); }); it('should handle single incident', () => { const incidents: PrometheusResult[] = [ { metric: { group_id: 'incident1', silenced: 'true' }, values: [[1000, '2']], }, ]; - const emptyTimestamps: IncidentsTimestamps = { - minOverTime: [], - lastOverTime: [], - }; - const result = processIncidentsForAlerts(incidents, emptyTimestamps);web/src/store/actions.ts (1)
193-197: Consider adding parameter types to the new action creators.Both
setIncidentsTimestampsandsetAlertsTimestampsaccept untyped parameters. While this is consistent with existing action creators in this file (e.g.,setIncidents,setAlertsData), addingIncidentsTimestamps/AlertsTimestampstypes would improve type safety and discoverability.web/src/components/Incidents/processAlerts.ts (2)
76-107: Gap-splitting logic is duplicated betweendeduplicateAlertsandsplitAbsoluteTimestampsByGap.Both iterate sorted values, detect gaps >
PROMETHEUS_QUERY_INTERVAL_SECONDS, and split into sub-intervals. Consider extracting a shared helper likesplitValuesByGap(sortedValues, threshold)to reduce duplication.Also applies to: 116-141
393-408: VariablefirstTimestampon line 395 shadows the outer scope and serves a different meaning.Line 395 assigns
firstTimestampfrom paddedValues (chart start position), while line 402 usesalert.firstTimestamp(absolute start). Both are named similarly but mean different things. Renaming the local tochartFirstTimestamporpaddedFirstTimestampwould improve readability.web/src/components/Incidents/IncidentsPage.tsx (2)
265-274: Alert timestamp values are reshaped to single-element arrays — verify downstream consumers expect this.The mapping on lines 271-273 transforms each alert value from
[queryTimestamp, alertTimestamp]to[roundedTimestamp](single-element array). Downstream code insplitAbsoluteTimestampsByGapandassignAbsoluteFirstTimestampsaccessesvalues[x][0], so this works — but the non-standard shape is fragile and undocumented. A comment clarifying whyvalue[1]is the actual alert firing timestamp (from thetimestamp()PromQL function) and whyvalue[0](the query evaluation time) is dropped would help future maintainers.
338-349: Hardcoded15dlookback for min_over_time query — should ideally align with the maximum selectable days filter.The Prometheus query
min_over_time(timestamp(cluster_health_components_map)[15d:5m])hardcodes a 15-day lookback. This happens to match the current max selectable range ("15 days"), but if the max range is ever extended, these will silently diverge. Consider extracting this as a constant.web/src/components/Incidents/utils.ts (1)
955-966: All parameters and return typed asany— limits type safety and IDE support.While defensively coded, this function could benefit from minimal typing. Even a simple interface for the
timestampmetric shape would catch mismatches (e.g., thevaluevsvaluesissue in tests).
| const response = await Promise.resolve(consoleFetchJSON(url)); | ||
| return { | ||
| status: 'success', | ||
| data: { | ||
| resultType: 'matrix', | ||
| result: response.data.result, | ||
| }, | ||
| } as PrometheusResponse; |
There was a problem hiding this comment.
Add defensive access on response.data.result to avoid potential TypeError.
If the API returns an unexpected shape (e.g., an error response without a data field), response.data.result at Line 185 will throw. The existing fetchDataForIncidentsAndAlerts (Line 145) uses optional chaining (r.data?.result || []) for this reason.
Also, Promise.resolve(consoleFetchJSON(url)) is redundant — consoleFetchJSON already returns a Promise.
Proposed fix
- const response = await Promise.resolve(consoleFetchJSON(url));
+ const response = await consoleFetchJSON(url);
return {
status: 'success',
data: {
resultType: 'matrix',
- result: response.data.result,
+ result: response?.data?.result || [],
},
} as PrometheusResponse;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const response = await Promise.resolve(consoleFetchJSON(url)); | |
| return { | |
| status: 'success', | |
| data: { | |
| resultType: 'matrix', | |
| result: response.data.result, | |
| }, | |
| } as PrometheusResponse; | |
| const response = await consoleFetchJSON(url); | |
| return { | |
| status: 'success', | |
| data: { | |
| resultType: 'matrix', | |
| result: response?.data?.result || [], | |
| }, | |
| } as PrometheusResponse; |
🤖 Prompt for AI Agents
In `@web/src/components/Incidents/api.ts` around lines 180 - 187, The code should
call consoleFetchJSON(url) directly (remove the redundant Promise.resolve) and
defensively access the result to avoid a TypeError: capture the response from
consoleFetchJSON and use response.data?.result || [] when constructing the
returned PrometheusResponse (keep the same shape with resultType: 'matrix'),
ensuring you still return the object as PrometheusResponse; reference the
variables/functions response, consoleFetchJSON and the PrometheusResponse return
shape and mirror the optional chaining used in fetchDataForIncidentsAndAlerts.
| const enrichedIncidentsData = useMemo(() => { | ||
| return incidentsData.map((incident) => { | ||
| // find the matched timestamp for the incident | ||
| const matchedMinTimestamp = matchTimestampMetricForIncident( | ||
| incident, | ||
| incidentsTimestamps.minOverTime, | ||
| ); | ||
|
|
||
| return { | ||
| ...incident, | ||
| firstTimestamp: parseInt(matchedMinTimestamp?.value?.[1] ?? '0'), | ||
| }; | ||
| }); | ||
| }, [incidentsData, incidentsTimestamps]); |
There was a problem hiding this comment.
firstTimestamp defaults to epoch (0) when no timestamp match is found, leading to "Jan 1, 1970" in tooltips.
When matchTimestampMetricForIncident returns undefined, parseInt(... ?? '0') yields 0. This propagates to createIncidentsChartBars (utils.ts line 350) as new Date(0 * 1000), displaying "Jan 1, 1970" in the tooltip's Start field. Consider falling back to the incident's earliest value timestamp instead.
Suggested approach
return {
...incident,
- firstTimestamp: parseInt(matchedMinTimestamp?.value?.[1] ?? '0'),
+ firstTimestamp: parseInt(matchedMinTimestamp?.value?.[1] ?? '0') || incident.values?.[0]?.[0] || 0,
};🤖 Prompt for AI Agents
In `@web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx` around lines
86 - 99, enrichedIncidentsData currently sets firstTimestamp to 0 when
matchTimestampMetricForIncident returns undefined, causing "Jan 1, 1970" in
tooltips; change the fallback in the useMemo mapping so firstTimestamp uses the
incident's earliest value timestamp (e.g., the first element in incident.values)
when matchedMinTimestamp is undefined, parsing that timestamp with parseInt
(instead of '0'), and keep the rest of the mapping unchanged; this touches
enrichedIncidentsData, matchTimestampMetricForIncident usage, and will prevent
createIncidentsChartBars from receiving epoch-zero values for firstTimestamp.
| const alertsTimestamps: AlertsTimestamps = { | ||
| minOverTime: [ | ||
| { | ||
| metric: { | ||
| alertname: 'TestAlert', | ||
| namespace: 'test-namespace', | ||
| severity: 'critical', | ||
| }, | ||
| value: [matchedMinTimestamp, matchedMinTimestamp.toString()], | ||
| }, | ||
| ], | ||
| lastOverTime: [], | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the test file around line 570-582 to see actual data structure
echo "=== Test file: alertsTimestamps at lines 570-582 ==="
sed -n '570,582p' web/src/components/Incidents/processAlerts.spec.ts
echo ""
echo "=== Context around line 570 to see full test ==="
sed -n '542,590p' web/src/components/Incidents/processAlerts.spec.ts
echo ""
echo "=== splitAbsoluteTimestampsByGap function in processAlerts.ts ==="
rg -n 'splitAbsoluteTimestampsByGap' web/src/components/Incidents/processAlerts.ts -A 20
echo ""
echo "=== Check production data shape in IncidentsPage.tsx lines 268-274 ==="
sed -n '265,280p' web/src/pages/IncidentsPage.tsxRepository: openshift/monitoring-plugin
Length of output: 4324
🏁 Script executed:
#!/bin/bash
# Find IncidentsPage.tsx in the repo
echo "=== Finding IncidentsPage.tsx ==="
find . -name "IncidentsPage.tsx" -o -name "IncidentsPage.ts" 2>/dev/null
echo ""
echo "=== Check AlertsTimestamps type definition ==="
rg -n 'type AlertsTimestamps|interface AlertsTimestamps' web/src/components/Incidents/ -A 5
echo ""
echo "=== Check where minOverTime is populated in tests ==="
rg -n 'minOverTime:' web/src/components/Incidents/processAlerts.spec.ts | head -20
echo ""
echo "=== Check the Prometheus query results shape vs alerts timestamps shape ==="
rg -n 'PrometheusResult' web/src/components/Incidents/processAlerts.ts -B 2 -A 8 | head -40Repository: openshift/monitoring-plugin
Length of output: 2418
🏁 Script executed:
#!/bin/bash
# Check how IncidentsPage.tsx populates alertsTimestamps, especially minOverTime
echo "=== IncidentsPage.tsx: where alertsTimestamps is created ==="
rg -n 'AlertsTimestamps\|minOverTime' web/src/components/Incidents/IncidentsPage.tsx -B 3 -A 10 | head -80
echo ""
echo "=== Check if there's a fetchDataForIncidentsAndAlerts function ==="
rg -n 'fetchDataForIncidentsAndAlerts\|fetchTimestamps' web/src/components/Incidents/ -A 10 | head -60Repository: openshift/monitoring-plugin
Length of output: 199
🏁 Script executed:
#!/bin/bash
# Let me search more broadly for alertsTimestamps usage in IncidentsPage
echo "=== All alertsTimestamps usage in IncidentsPage ==="
rg -n 'alertsTimestamps' web/src/components/Incidents/IncidentsPage.tsx -B 2 -A 2
echo ""
echo "=== Check convertToAlerts function signature ==="
rg -n 'export.*convertToAlerts|function convertToAlerts' web/src/components/Incidents/processAlerts.ts -A 5
echo ""
echo "=== Check how the minOverTime/lastOverTime data is fetched/transformed ==="
rg -n 'const.*minOverTime\|const.*lastOverTime' web/src/components/Incidents/ -B 2 -A 5 | head -100
echo ""
echo "=== Look at all the tests to understand the pattern ==="
sed -n '541,730p' web/src/components/Incidents/processAlerts.spec.ts | grep -n 'minOverTime\|value:' | head -30Repository: openshift/monitoring-plugin
Length of output: 1151
🏁 Script executed:
#!/bin/bash
# Let me check what fetchedAlertsTimestamps contains
echo "=== Search for where fetchedAlertsTimestamps is created ==="
rg -n 'fetchedAlertsTimestamps\|setAlertsTimestamps' web/src/components/Incidents/ -B 5 -A 5
echo ""
echo "=== Check the exact line in IncidentsPage where alertsTimestamps is set ==="
sed -n '275,290p' web/src/components/Incidents/IncidentsPage.tsx
echo ""
echo "=== Look for API calls that fetch timestamp data ==="
rg -n 'api\|fetch.*timestamp\|query.*timestamp' web/src/components/Incidents/IncidentsPage.tsx -B 2 -A 5 | head -100Repository: openshift/monitoring-plugin
Length of output: 702
🏁 Script executed:
#!/bin/bash
# Check where roundedTimestamps comes from
echo "=== Search for roundedTimestamps ==="
rg -n 'roundedTimestamps' web/src/components/Incidents/IncidentsPage.tsx -B 5 -A 2
echo ""
echo "=== Get more context around line 280 ==="
sed -n '265,295p' web/src/components/Incidents/IncidentsPage.tsx
echo ""
echo "=== Check the structure of timestampsResults ==="
rg -n 'timestampsResults' web/src/components/Incidents/IncidentsPage.tsx -B 3 -A 3Repository: openshift/monitoring-plugin
Length of output: 3132
🏁 Script executed:
#!/bin/bash
# This is the key - let me compare the two data shapes side by side
echo "=== CASE 1: Alerts timestamp (lines 268-274) - what production creates ==="
sed -n '268,274p' web/src/components/Incidents/IncidentsPage.tsx
echo ""
echo "=== CASE 2: Incidents timestamp (lines 354-361) - different shape ==="
sed -n '354,361p' web/src/components/Incidents/IncidentsPage.tsx
echo ""
echo "=== TEST: What shape does the test use? (line 571-578) ==="
sed -n '571,578p' web/src/components/Incidents/processAlerts.spec.ts
echo ""
echo "=== Now let's verify: what does splitAbsoluteTimestampsByGap expect? ==="
sed -n '116,140p' web/src/components/Incidents/processAlerts.tsRepository: openshift/monitoring-plugin
Length of output: 2043
🏁 Script executed:
#!/bin/bash
# Let me check if splitAbsoluteTimestampsByGap is called with both minOverTime and lastOverTime
echo "=== Where is splitAbsoluteTimestampsByGap called? ==="
rg -n 'splitAbsoluteTimestampsByGap' web/src/components/Incidents/processAlerts.ts -B 2 -A 2
echo ""
echo "=== Let's trace convertToAlerts to see what it does with alertsTimestamps ==="
sed -n '328,365p' web/src/components/Incidents/processAlerts.ts
echo ""
echo "=== Does the test actually run convertToAlerts? Check the test ==="
sed -n '542,590p' web/src/components/Incidents/processAlerts.spec.ts | tail -20Repository: openshift/monitoring-plugin
Length of output: 3367
🏁 Script executed:
#!/bin/bash
# Let me run the actual test to see if it fails
echo "=== Check if there's a test runner setup and try running this specific test ==="
ls -la web/src/components/Incidents/processAlerts.spec.ts
echo ""
echo "=== Check what happens when we trace through with test data ==="
echo "Test provides: value: [matchedMinTimestamp, ...]"
echo "Code does: [...entry.values] which is [...undefined]"
echo "This should throw TypeError: undefined is not iterable"
echo ""
echo "=== Let me check if there's any transformation or if both .value and .values are supported ==="
rg -n 'entry\.value\b|\.value\[' web/src/components/Incidents/processAlerts.ts | head -20
echo ""
echo "=== Also check assignAbsoluteFirstTimestamps to see if it handles .value ==="
rg -n 'assignAbsoluteFirstTimestamps' web/src/components/Incidents/processAlerts.ts -A 30 | head -50Repository: openshift/monitoring-plugin
Length of output: 2951
🏁 Script executed:
#!/bin/bash
# Let me verify the exact data flow - check if test data matches production data shape
echo "=== Production shape for minOverTime (from IncidentsPage line 268-274) ==="
echo "Structure: entries with .values (plural) containing array of arrays"
echo ""
echo "=== Test shape for minOverTime (from processAlerts.spec.ts line 571-578) ==="
echo "Structure: entries with .value (singular) containing single array"
echo ""
echo "=== What splitAbsoluteTimestampsByGap expects (line 116-121) ==="
sed -n '116,121p' web/src/components/Incidents/processAlerts.ts
echo ""
echo "=== Confirm: .values is never optional or checked for existence ==="
rg -n 'entry\.values' web/src/components/Incidents/processAlerts.ts
echo ""
echo "=== Check if maybe .value is ever converted to .values in tests ==="
rg -n 'value:.*\[' web/src/components/Incidents/processAlerts.spec.ts | head -10Repository: openshift/monitoring-plugin
Length of output: 1186
Test data uses .value (singular) but splitAbsoluteTimestampsByGap reads .values (plural) — tests will fail at runtime.
The alertsTimestamps.minOverTime entries in this test (and the three following timestamp-matching tests at lines 589–638, 640–687, 689–729) use value: [matchedMinTimestamp, ...], but splitAbsoluteTimestampsByGap (line 118) performs [...entry.values], which will throw TypeError: undefined is not iterable since .values does not exist on the test objects.
Production code in IncidentsPage.tsx (lines 268–274) correctly generates entries with .values: result.values.map(...) (array of arrays), whereas the test data provides a single array pair. Update test entries to use values: [[matchedMinTimestamp, matchedMinTimestamp.toString()]] to match the production data shape.
🤖 Prompt for AI Agents
In `@web/src/components/Incidents/processAlerts.spec.ts` around lines 570 - 582,
The test fixtures for AlertsTimestamps are using key "value" (singular) but
splitAbsoluteTimestampsByGap expects entries shaped like production (entries
have "values" as an array of [timestamp,string] pairs); update the test objects
in alertsTimestamps.minOverTime (and the three other timestamp-matching tests)
to use values: [[matchedMinTimestamp, matchedMinTimestamp.toString()]] (i.e.,
change "value: [...]" to "values: [[...]]") so they match the shape consumed by
splitAbsoluteTimestampsByGap and the AlertsTimestamps type.
| ); | ||
|
|
||
| const incidentFirstTimestamp = Math.min(...timestamps); | ||
| const incidentFirstTimestamp = incidents[0].firstTimestamp; |
There was a problem hiding this comment.
Potential TypeError if incidents array is empty after merging.
incidents[0].firstTimestamp will throw if mergeIncidentsByKey returns an empty array. While the caller in IncidentsPage.tsx guards against an empty incidentForAlertProcessing, this function is public and should be defensive.
Proposed fix
- const incidentFirstTimestamp = incidents[0].firstTimestamp;
+ const incidentFirstTimestamp = incidents[0]?.firstTimestamp ?? 0;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const incidentFirstTimestamp = incidents[0].firstTimestamp; | |
| const incidentFirstTimestamp = incidents[0]?.firstTimestamp ?? 0; |
🤖 Prompt for AI Agents
In `@web/src/components/Incidents/processAlerts.ts` at line 344, The code assumes
incidents[0] exists when computing incidentFirstTimestamp in processAlerts
(after calling mergeIncidentsByKey), which can throw if the merged incidents
array is empty; update the public function in processAlerts.ts to defensively
handle an empty array by checking incidents.length (or the result of
mergeIncidentsByKey) before accessing incidents[0].firstTimestamp and either
return early/skip processing, or set incidentFirstTimestamp to a safe default
(e.g., undefined) and propagate that state so callers won’t dereference a
missing element; reference the variable incidentFirstTimestamp and the
mergeIncidentsByKey call to find where to add the guard.
| export const processIncidentsForAlerts = ( | ||
| incidents: Array<PrometheusResult>, | ||
| ): Array<Partial<Incident>> => { | ||
| return incidents.map((incident, index) => { | ||
| incidentsTimestamps: IncidentsTimestamps, | ||
| ) => { | ||
| const matchedIncidents = incidents.map((incident) => { | ||
| // expand matchTimestampMetricForIncident here | ||
| const matchedMinTimestamp = matchTimestampMetricForIncident( | ||
| incident.metric, | ||
| incidentsTimestamps.minOverTime, | ||
| ); | ||
|
|
||
| return { | ||
| ...incident, | ||
| firstTimestamp: parseInt(matchedMinTimestamp?.value?.[1] ?? '0'), | ||
| }; | ||
| }); | ||
|
|
||
| return matchedIncidents.map((incident, index) => { | ||
| // Read silenced value from cluster_health_components_map metric label | ||
| // If missing, default to false | ||
| const silenced = incident.metric.silenced === 'true'; | ||
|
|
||
| // Return the processed incident | ||
| return { | ||
| const retval = { | ||
| ...incident.metric, | ||
| values: incident.values, | ||
| x: incidents.length - index, | ||
| silenced, | ||
| firstTimestamp: incident.firstTimestamp, | ||
| }; | ||
| return retval; | ||
| }); |
There was a problem hiding this comment.
Same epoch-fallback issue: firstTimestamp defaults to 0 when no timestamp match is found.
Line 207 uses the same parseInt(matchedMinTimestamp?.value?.[1] ?? '0') pattern already flagged in IncidentsChart.tsx. When no match exists, firstTimestamp becomes 0 (epoch), which propagates misleading dates into downstream alert processing. Consider using the incident's earliest value timestamp as a fallback here too.
🤖 Prompt for AI Agents
In `@web/src/components/Incidents/processIncidents.ts` around lines 194 - 225,
processIncidentsForAlerts currently falls back to '0' when
matchTimestampMetricForIncident returns no match, producing epoch timestamps;
change the fallback for firstTimestamp to use the incident's earliest value
timestamp (from incident.values) before finally defaulting to '0'. Specifically,
when computing firstTimestamp in processIncidentsForAlerts, pick
matchedMinTimestamp?.value?.[1] if present, otherwise use the timestamp from
incident.values (the earliest entry), and then parseInt that result to set
firstTimestamp; update any parseInt usage accordingly to avoid defaulting
straight to epoch.
| const incident = { | ||
| group_id: 'group1', | ||
| src_alertname: 'Alert1', | ||
| src_namespace: 'ns1', | ||
| component: 'comp1', | ||
| src_severity: 'warning', | ||
| }; | ||
|
|
||
| const timestamps = [ | ||
| { | ||
| metric: { | ||
| group_id: 'group1', | ||
| src_alertname: 'Alert1', | ||
| src_namespace: 'ns1', | ||
| component: 'comp1', | ||
| src_severity: 'warning', | ||
| }, | ||
| value: [1704067200, '1704067200'], | ||
| }, | ||
| ]; | ||
|
|
||
| const result = matchTimestampMetricForIncident(incident, timestamps); | ||
| expect(result).toBe(timestamps[0]); | ||
| }); |
There was a problem hiding this comment.
Test assertions and it blocks are incorrectly scoped — assertions at describe level, subsequent tests orphaned.
Lines 156–178 contain variable declarations and expect assertions directly inside the describe('matchTimestampMetricForIncident') body (not wrapped in an it block). These will execute during Jest's collection phase, not as a proper test. Additionally, the describe closes at line 179, so the it blocks on lines 181–270 become top-level tests, detached from their intended describe group.
This looks like a missing it(...) wrapper around lines 156–178.
Proposed fix
- const incident = {
+ it('should match when all fields match exactly', () => {
+ const incident = {
group_id: 'group1',
src_alertname: 'Alert1',
src_namespace: 'ns1',
component: 'comp1',
src_severity: 'warning',
};
const timestamps = [
{
metric: {
group_id: 'group1',
src_alertname: 'Alert1',
src_namespace: 'ns1',
component: 'comp1',
src_severity: 'warning',
},
value: [1704067200, '1704067200'],
},
];
const result = matchTimestampMetricForIncident(incident, timestamps);
expect(result).toBe(timestamps[0]);
-});
+ });
-it('should not match when group_id differs', () => {
+ it('should not match when group_id differs', () => {And similarly re-indent the remaining it blocks (lines 181–270) to be inside the describe by moving the closing }); of the describe to after line 270.
🤖 Prompt for AI Agents
In `@web/src/components/Incidents/utils.spec.ts` around lines 156 - 179, The test
setup and assertion for matchTimestampMetricForIncident are placed directly in
the describe block, causing them to run during collection and orphaning
subsequent it blocks; wrap the variable declarations (incident, timestamps,
result) and the expect(result).toBe(...) assertion inside a new it('returns
matching timestamp metric', () => { ... }) block, and move the describe closing
token (the final "});") to after the existing it blocks so all it tests
(including those on lines 181–270) are nested inside
describe('matchTimestampMetricForIncident'); keep references to
matchTimestampMetricForIncident, incident, timestamps, and result when
relocating code.
| startDate: Date; | ||
| fill: string; |
There was a problem hiding this comment.
Same concern: startDate derived from firstTimestamp which may be epoch (0) for unmatched incidents.
new Date(incident.firstTimestamp * 1000) on line 350 will produce "Jan 1, 1970" if firstTimestamp is 0 (the default when no timestamp match is found). This is the display-side of the issue flagged in IncidentsChart.tsx.
Also applies to: 350-350
🤖 Prompt for AI Agents
In `@web/src/components/Incidents/utils.ts` around lines 325 - 326, The code
constructs startDate from incident.firstTimestamp using new
Date(incident.firstTimestamp * 1000) which yields 1970-01-01 for a 0 timestamp;
change the construction so that when incident.firstTimestamp is missing/0 you
set startDate to null (or undefined) instead of a Date(0), update the startDate
type to Date | null in the relevant interface/shape (the startDate property in
the object/type), and adjust consumers (e.g., IncidentsChart.tsx and any
rendering logic) to handle null startDate appropriately; specifically locate the
new Date(...) call and replace it with a guarded expression that only creates a
Date when incident.firstTimestamp > 0 and otherwise assigns null, and update the
type annotations for startDate to match.
1d58a55 to
ceb6fcd
Compare
|
@rioloc: The following tests failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
Problem
Startdates in tooltips are relatives to the numer of selected days as time span.For example, if today is 30 Jan 2026 and an incident started on 22 Jan 2026, the
Startdate will be displayed as:Fix
The absolute start date of an incident/alert is always displayed, and it is not related to the number of selected days.
Solution
Absolute timestamps for
cluster_health_components_map{}, for incidents, andALERTS{}for alerts are retrieved by performing an instant query call to Prometheus in order to get themin_over_time(timestamp(cluster_health_components_map{})), which return the timestamp of the first datapoint for that metric. (Same for ALERTS).The result is saved into redux store and then used to match related incident/alert in order to update the Start date displayed in the tooltip.
Before
main.webm
After
feat.absolute-start-dates.webm
Summary by CodeRabbit
Bug Fixes
New Features