Skip to content

OU-1040: feat/absolute start dates#749

Open
rioloc wants to merge 10 commits intoopenshift:mainfrom
rioloc:feat/absolute-start-dates
Open

OU-1040: feat/absolute start dates#749
rioloc wants to merge 10 commits intoopenshift:mainfrom
rioloc:feat/absolute-start-dates

Conversation

@rioloc
Copy link
Member

@rioloc rioloc commented Jan 30, 2026

Problem

Start dates 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 Start date will be displayed as:

  • 29 Jan 2026 if "Last 1 Day" is selected
  • 27 Jan 2026 if "Last 3 Days" is selected
  • 23 Jan 2026 if "Last 7 Days" is selected
  • 22 Jan 2026 if "Last 15 Days" is selected. (The only correct one)

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, and ALERTS{} for alerts are retrieved by performing an instant query call to Prometheus in order to get the min_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

    • Enhanced timestamp accuracy for alert and incident start and end times by using more precise underlying data sources instead of calculated values.
  • New Features

    • Alerts are now grouped and aggregated by their name, namespace, and severity level, consolidating identical alerts into shared rows on the incidents chart.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jan 30, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Jan 30, 2026

@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.

Details

In response to this:

Problem

Start dates 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 Start date will be displayed as:

  • 29 Jan 2026 if "Last 1 Day" is selected
  • 27 Jan 2026 if "Last 3 Days" is selected
  • 23 Jan 2026 if "Last 7 Days" is selected
  • 22 Jan 2026 if "Last 15 Days" is selected. (The only correct one)

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, and ALERTS{} for alerts are retrieved by performoring an instant query call to Prometheus in order to get the min_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 then update the Start date displayed in the tooltip.

Before

main.webm

After

feat.absolute-start-dates.webm

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.

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jan 30, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 30, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci-robot
Copy link

openshift-ci-robot commented Jan 30, 2026

@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.

Details

In response to this:

Problem

Start dates 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 Start date will be displayed as:

  • 29 Jan 2026 if "Last 1 Day" is selected
  • 27 Jan 2026 if "Last 3 Days" is selected
  • 23 Jan 2026 if "Last 7 Days" is selected
  • 22 Jan 2026 if "Last 15 Days" is selected. (The only correct one)

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, and ALERTS{} for alerts are retrieved by performing an instant query call to Prometheus in order to get the min_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 then update the Start date displayed in the tooltip.

Before

main.webm

After

feat.absolute-start-dates.webm

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.

@openshift-ci-robot
Copy link

openshift-ci-robot commented Jan 30, 2026

@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.

Details

In response to this:

Problem

Start dates 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 Start date will be displayed as:

  • 29 Jan 2026 if "Last 1 Day" is selected
  • 27 Jan 2026 if "Last 3 Days" is selected
  • 23 Jan 2026 if "Last 7 Days" is selected
  • 22 Jan 2026 if "Last 15 Days" is selected. (The only correct one)

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, and ALERTS{} for alerts are retrieved by performing an instant query call to Prometheus in order to get the min_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

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.

@DavidRajnoha
Copy link
Contributor

/test e2e-incidents

@rioloc rioloc marked this pull request as ready for review February 2, 2026 09:22
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 2, 2026
@DavidRajnoha
Copy link
Contributor

/test e2e-incidents

@DavidRajnoha
Copy link
Contributor

/label qe-approved

@openshift-ci openshift-ci bot added the qe-approved Signifies that QE has signed off on this PR label Feb 5, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 5, 2026

@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.

Details

In response to this:

Problem

Start dates 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 Start date will be displayed as:

  • 29 Jan 2026 if "Last 1 Day" is selected
  • 27 Jan 2026 if "Last 3 Days" is selected
  • 23 Jan 2026 if "Last 7 Days" is selected
  • 22 Jan 2026 if "Last 15 Days" is selected. (The only correct one)

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, and ALERTS{} for alerts are retrieved by performing an instant query call to Prometheus in order to get the min_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

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
@rioloc rioloc force-pushed the feat/absolute-start-dates branch from 14440d2 to b8f4ed7 Compare February 6, 2026 09:14
@rioloc
Copy link
Member Author

rioloc commented Feb 6, 2026

/test e2e-incidents

@rioloc
Copy link
Member Author

rioloc commented Feb 6, 2026

/retest

2 similar comments
@etmurasaki
Copy link
Contributor

/retest

@rioloc
Copy link
Member Author

rioloc commented Feb 6, 2026

/retest

@etmurasaki
Copy link
Contributor

/retest

@rioloc
Copy link
Member Author

rioloc commented Feb 9, 2026

/hold

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Feb 9, 2026
@rioloc
Copy link
Member Author

rioloc commented Feb 9, 2026

/unhold

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Feb 9, 2026
@DavidRajnoha
Copy link
Contributor

/label qe-approved

@PeterYurkovich
Copy link
Contributor

/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Feb 9, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 9, 2026

[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

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 9, 2026
@rioloc
Copy link
Member Author

rioloc commented Feb 10, 2026

/hold

@openshift-ci openshift-ci bot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. and removed lgtm Indicates that a PR is ready to be merged. labels Feb 10, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 10, 2026

New changes are detected. LGTM label has been removed.

@etmurasaki
Copy link
Contributor

/retest

@rioloc rioloc force-pushed the feat/absolute-start-dates branch from 3f05003 to 8b40410 Compare February 11, 2026 00:40
@rioloc rioloc force-pushed the feat/absolute-start-dates branch from 8b40410 to a5864d2 Compare February 11, 2026 00:48
@etmurasaki
Copy link
Contributor

/test okd-scos-images

@etmurasaki
Copy link
Contributor

linter error, please take a look

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 16, 2026
@openshift-merge-robot
Copy link
Contributor

PR needs rebase.

Details

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 kubernetes-sigs/prow repository.

@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 16, 2026

@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.

Details

In response to this:

Problem

Start dates 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 Start date will be displayed as:

  • 29 Jan 2026 if "Last 1 Day" is selected
  • 27 Jan 2026 if "Last 3 Days" is selected
  • 23 Jan 2026 if "Last 7 Days" is selected
  • 22 Jan 2026 if "Last 15 Days" is selected. (The only correct one)

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, and ALERTS{} for alerts are retrieved by performing an instant query call to Prometheus in order to get the min_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
  • Enhanced timestamp accuracy for alert and incident start and end times by using more precise underlying data sources instead of calculated values.
  • New Features
  • Alerts are now grouped and aggregated by their name, namespace, and severity level, consolidating identical alerts into shared rows on the incidents chart.

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.

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

📝 Walkthrough

Walkthrough

This pull request introduces timestamp-aware data enrichment for incidents and alerts. The changes add new API calls to fetch instantaneous timestamp metrics, compute firstTimestamp and lastTimestamp for incidents and alerts, and propagate this enriched data through state management, chart rendering, and table display, enabling more accurate time-based sorting and visualization.

Changes

Cohort / File(s) Summary
Data Model & State Schema
web/src/components/Incidents/model.ts, web/src/store/store.ts
Extended Incident, Alert, and IncidentsDetailsAlert types with firstTimestamp and lastTimestamp fields; introduced new IncidentsTimestamps and AlertsTimestamps types; initialized new timestamp fields in store state.
Redux Actions & Reducers
web/src/store/actions.ts, web/src/store/reducers.ts
Added SetIncidentsTimestamps and SetAlertsTimestamps action types; created action creators to dispatch timestamp payloads; added reducer cases to update store state.
API & Data Fetching
web/src/components/Incidents/api.ts
Introduced new fetchInstantData function to retrieve Prometheus instantaneous query results; mirrors existing range-query patterns.
Incident & Alert Processing
web/src/components/Incidents/processIncidents.ts, web/src/components/Incidents/processAlerts.ts
Updated processIncidentsForAlerts signature to accept incidentsTimestamps; enhanced convertToAlerts to compute firstTimestamp via timestamp matching and gap-aware splitting; added splitAbsoluteTimestampsByGap and assignAbsoluteFirstTimestamps helpers for interval-based processing.
Chart & Utility Functions
web/src/components/Incidents/utils.ts
Added roundTimestampToFiveMinutes and matchTimestampMetricForIncident utilities; refactored consolidateAndMergeAlertIntervals and createAlertsChartBars to accept alert arrays and compute startDate; extended createIncidentsChartBars to include startDate from firstTimestamp; updated generateAlertsDateArray with 5% padding logic.
Chart Components
web/src/components/Incidents/AlertsChart/AlertsChart.tsx, web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx
Updated alert grouping to aggregate by composite key; added incidentsTimestamps prop to IncidentsChart; changed tooltip date formatting to use startDate directly instead of deriving from y0.
Table Components
web/src/components/Incidents/IncidentsTable.tsx, web/src/components/Incidents/IncidentsDetailsRowTable.tsx
Updated sorting and rendering logic to prefer firstTimestamp/lastTimestamp with fallback to alertsStartFiring/alertsEndFiring; adjusted composite timestamp selection for start and end times.
Main Page Orchestration
web/src/components/Incidents/IncidentsPage.tsx
Refactored data fetching to parallelize timestamp and data retrieval; added calls to setIncidentsTimestamps and setAlertsTimestamps actions; passed fetched timestamps into alert/incident processors; updated IncidentsChart with incidentsTimestamps prop; converted console.log to console.error for error handling.
Test Coverage
web/src/components/Incidents/processAlerts.spec.ts, web/src/components/Incidents/processIncidents.spec.ts, web/src/components/Incidents/utils.spec.ts
Expanded test suites with timestamp matching scenarios, gap splitting behavior, utility function validation (roundTimestampToFiveMinutes, matchTimestampMetricForIncident, insertPaddingPointsForChart), edge cases, and emptyTimestamps fixtures; updated function call sites to pass new timestamp parameters.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A timestamp odyssey we weave,
First and last shall now perceive,
Gaps are split with grace so keen,
Charts align to what they've seen,
Sorted true from start to end,
Time's precision we commend! 🕐✨

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (55 files):

⚔️ .gitignore (content)
⚔️ config/perses-dashboards.patch.json (content)
⚔️ web/console-extensions.json (content)
⚔️ web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts (content)
⚔️ web/cypress/e2e/incidents/00.coo_incidents_e2e.cy.ts (content)
⚔️ web/cypress/e2e/incidents/regression/03-04.reg_e2e_firing_alerts.cy.ts (content)
⚔️ web/cypress/e2e/perses/01.coo_list_perses_admin.cy.ts (content)
⚔️ web/cypress/fixtures/coo/acm-install.sh (content)
⚔️ web/cypress/fixtures/perses/constants.ts (content)
⚔️ web/cypress/support/commands/operator-commands.ts (content)
⚔️ web/cypress/support/index.ts (content)
⚔️ web/cypress/support/perses/00.coo_bvt_perses_admin_1.cy.ts (content)
⚔️ web/cypress/support/perses/01.coo_list_perses_admin.cy.ts (content)
⚔️ web/cypress/support/perses/01.coo_list_perses_admin_namespace.cy.ts (content)
⚔️ web/cypress/support/perses/02.coo_edit_perses_admin.cy.ts (content)
⚔️ web/cypress/support/perses/02.coo_edit_perses_admin_1.cy.ts (content)
⚔️ web/cypress/support/perses/03.coo_create_perses_admin.cy.ts (content)
⚔️ web/cypress/support/perses/99.coo_rbac_perses_user1.cy.ts (content)
⚔️ web/cypress/support/perses/99.coo_rbac_perses_user2.cy.ts (content)
⚔️ web/cypress/views/perses-dashboards.ts (content)
⚔️ web/package-lock.json (content)
⚔️ web/package.json (content)
⚔️ web/src/components/Incidents/AlertsChart/AlertsChart.tsx (content)
⚔️ web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx (content)
⚔️ web/src/components/Incidents/IncidentsDetailsRowTable.tsx (content)
⚔️ web/src/components/Incidents/IncidentsPage.tsx (content)
⚔️ web/src/components/Incidents/IncidentsTable.tsx (content)
⚔️ web/src/components/Incidents/api.ts (content)
⚔️ web/src/components/Incidents/model.ts (content)
⚔️ web/src/components/Incidents/processAlerts.spec.ts (content)
⚔️ web/src/components/Incidents/processAlerts.ts (content)
⚔️ web/src/components/Incidents/processIncidents.spec.ts (content)
⚔️ web/src/components/Incidents/processIncidents.ts (content)
⚔️ web/src/components/Incidents/utils.spec.ts (content)
⚔️ web/src/components/Incidents/utils.ts (content)
⚔️ web/src/components/dashboards/legacy/graph.tsx (content)
⚔️ web/src/components/dashboards/perses/PersesWrapper.tsx (content)
⚔️ web/src/components/dashboards/perses/dashboard-page.tsx (content)
⚔️ web/src/components/dashboards/perses/datasource-api.ts (content)
⚔️ web/src/components/dashboards/perses/hooks/useDashboardsData.ts (content)
⚔️ web/src/components/dashboards/perses/hooks/usePerses.ts (content)
⚔️ web/src/components/dashboards/perses/perses-client.ts (content)
⚔️ web/src/components/dashboards/perses/project/ProjectBar.tsx (content)
⚔️ web/src/components/dashboards/perses/project/ProjectDropdown.tsx (content)
⚔️ web/src/components/dashboards/perses/project/useActiveProject.tsx (content)
⚔️ web/src/components/dashboards/perses/project/utils.ts (content)
⚔️ web/src/components/dashboards/shared/dashboard-dropdown.tsx (content)
⚔️ web/src/components/data-test.ts (content)
⚔️ web/src/components/hooks/usePerspective.tsx (content)
⚔️ web/src/components/metrics/promql-expression-input.tsx (content)
⚔️ web/src/index.d.ts (content)
⚔️ web/src/store/actions.ts (content)
⚔️ web/src/store/reducers.ts (content)
⚔️ web/src/store/store.ts (content)
⚔️ web/webpack.config.ts (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'OU-1040: feat/absolute start dates' clearly and specifically describes the main change: implementing absolute start dates for incidents and alerts instead of relative dates.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch feat/absolute-start-dates
  • Post resolved changes as copyable diffs in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟠 Major

Missing fallback when firstTimestamp is 0.

When no timestamp match is found, firstTimestamp defaults to 0, causing getMinStartDate to return 0 — which renders as Jan 1, 1970 in the <Timestamp> component (Line 183) and breaks sort order. IncidentsDetailsRowTable.tsx (Lines 28-29) guards against this with firstTimestamp > 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 | 🟡 Minor

Inconsistent error logging: console.log here vs console.error in the alerts fetch (line 313).

The alerts-fetching .catch on line 313 uses console.error but the incidents-fetching .catch here still uses console.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 | 🟠 Major

Update AlertsChartBar type definition to include startDate field, or remove it from the function if unused.

The createAlertsChartBars function (line 430) adds startDate: new Date(intervalFirstTimestamp * 1000) to each returned object, but the AlertsChartBar type definition in model.ts (lines 137-150) does not include this field. The function has an explicit return type annotation of AlertsChartBar[], creating a type mismatch. Additionally, startDate is not accessed anywhere in the codebase, suggesting it may be dead code. Either add startDate: Date to the AlertsChartBar type 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 replacing Array<any> with a proper type for Prometheus instant query results.

Both IncidentsTimestamps and AlertsTimestamps use Array<any> for minOverTime and lastOverTime. The data shape from Prometheus instant queries is well-known (an object with metric: Record<string, string> and value: [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: fetch parameter is accepted but unused.

The fetch parameter is never referenced in the function body — consoleFetchJSON is called directly on Line 180. This matches the pattern in fetchDataForIncidentsAndAlerts (which also accepts but ignores fetch), 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: Redundant emptyTimestamps re-declarations in edge case tests.

The emptyTimestamps fixture at Lines 1039-1042 and 1055-1058 duplicates the one already declared in the parent describe scope (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 setIncidentsTimestamps and setAlertsTimestamps accept untyped parameters. While this is consistent with existing action creators in this file (e.g., setIncidents, setAlertsData), adding IncidentsTimestamps / AlertsTimestamps types would improve type safety and discoverability.

web/src/components/Incidents/processAlerts.ts (2)

76-107: Gap-splitting logic is duplicated between deduplicateAlerts and splitAbsoluteTimestampsByGap.

Both iterate sorted values, detect gaps > PROMETHEUS_QUERY_INTERVAL_SECONDS, and split into sub-intervals. Consider extracting a shared helper like splitValuesByGap(sortedValues, threshold) to reduce duplication.

Also applies to: 116-141


393-408: Variable firstTimestamp on line 395 shadows the outer scope and serves a different meaning.

Line 395 assigns firstTimestamp from paddedValues (chart start position), while line 402 uses alert.firstTimestamp (absolute start). Both are named similarly but mean different things. Renaming the local to chartFirstTimestamp or paddedFirstTimestamp would 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 in splitAbsoluteTimestampsByGap and assignAbsoluteFirstTimestamps accesses values[x][0], so this works — but the non-standard shape is fragile and undocumented. A comment clarifying why value[1] is the actual alert firing timestamp (from the timestamp() PromQL function) and why value[0] (the query evaluation time) is dropped would help future maintainers.


338-349: Hardcoded 15d lookback 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 as any — limits type safety and IDE support.

While defensively coded, this function could benefit from minimal typing. Even a simple interface for the timestamp metric shape would catch mismatches (e.g., the value vs values issue in tests).

Comment on lines +180 to +187
const response = await Promise.resolve(consoleFetchJSON(url));
return {
status: 'success',
data: {
resultType: 'matrix',
result: response.data.result,
},
} as PrometheusResponse;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment on lines 86 to 99
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]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +570 to +582
const alertsTimestamps: AlertsTimestamps = {
minOverTime: [
{
metric: {
alertname: 'TestAlert',
namespace: 'test-namespace',
severity: 'critical',
},
value: [matchedMinTimestamp, matchedMinTimestamp.toString()],
},
],
lastOverTime: [],
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.tsx

Repository: 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 -40

Repository: 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 -60

Repository: 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 -30

Repository: 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 -100

Repository: 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 3

Repository: 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.ts

Repository: 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 -20

Repository: 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 -50

Repository: 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 -10

Repository: 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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment on lines 194 to 225
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;
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +156 to +179
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]);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +325 to 326
startDate: Date;
fill: string;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

@rioloc rioloc force-pushed the feat/absolute-start-dates branch from 1d58a55 to ceb6fcd Compare February 16, 2026 17:39
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 16, 2026

@rioloc: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-incidents b8f4ed7 link false /test e2e-incidents
ci/prow/verify-deps ceb6fcd link true /test verify-deps
ci/prow/e2e-aws-ovn ceb6fcd link true /test e2e-aws-ovn
ci/prow/okd-scos-images ceb6fcd link true /test okd-scos-images
ci/prow/images ceb6fcd link true /test images
ci/prow/lint ceb6fcd link true /test lint
ci/prow/translations ceb6fcd link true /test translations
ci/prow/e2e-monitoring ceb6fcd link false /test e2e-monitoring
ci/prow/periodics-images ceb6fcd link true /test periodics-images

Full PR test history. Your PR dashboard.

Details

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 kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. qe-approved Signifies that QE has signed off on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants