SRVKP-12328: Add date range filter for PipelineRuns list page#1133
SRVKP-12328: Add date range filter for PipelineRuns list page#1133adityavshinde wants to merge 1 commit into
Conversation
Code Review by Qodo
1. Missing i18n key Time range
|
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: adityavshinde The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
b44e712 to
24a8a9b
Compare
anwesha-palit-redhat
left a comment
There was a problem hiding this comment.
- Disable tekton-results from tektonconfig and check the behave and attach the screen recording
- The screen recording you currently added is not clear - unable to understand the flow of what you are trying to show
- Add some AI generated unit test cases for this change please
1. Screen recording after disabling tekton-results from tektonconfigRFE_without_tkn-results.mp42. Steps followed in the screen recording (description)
3. Unit testsAlready added in |
|
| const trOptions: typeof optionsMemo = useMemo(() => { | ||
| if (optionsMemo?.name) { | ||
| const { name, ...rest } = optionsMemo; | ||
| const { name, filter, ...rest } = optionsMemo; |
There was a problem hiding this comment.
this needs to be rebased with master branch
There was a problem hiding this comment.
is this rebased yet ?
There was a problem hiding this comment.
Yes, I have rebased this. Now making changes accordingly.
| placeholder?: string; | ||
| options: FilterOption[]; | ||
| defaultValues?: string[]; | ||
| singleSelect?: boolean; |
There was a problem hiding this comment.
what is the purpose of single select
There was a problem hiding this comment.
The existing DataViewFilterToolbar only supported checkbox-style filters where users can select multiple values (e.g., selecting both "Succeeded" and "Failed" statuses). But for the date range filter, selecting multiple time ranges simultaneously doesn't make sense.
|
@adityavshinde: This pull request references SRVKP-12328 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 story to target the "5.0.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. |
| limit?: number; | ||
| name?: string; | ||
| skipFetch?: boolean; | ||
| filter?: string; |
There was a problem hiding this comment.
- Server-side filtering via CEL expression for Tekton Results data
- Client-side filtering for Kubernetes/etcd data
can you capture screen recording with network tab to verify if server side filtering is working for tekton-results api
There was a problem hiding this comment.
Server side filtering is working for tekton-results api but for K8s api we do not have server side filtering.
Screencast.From.2026-06-19.13-39-30.mp4
There was a problem hiding this comment.
also add a single line comment for what the filter means here
e698165 to
aa972d4
Compare
|
Code review by qodo was updated up to the latest commit aa972d4 |
| return { | ||
| ...rest, | ||
| filter: EQ('data.metadata.name', name), | ||
| filter: AND(EQ('data.metadata.name', name), filter), |
There was a problem hiding this comment.
what if filter is null ?
There was a problem hiding this comment.
what is the expected format for the filter
There was a problem hiding this comment.
what if
filteris null ?
AND() is a utility function in tekton-results.ts that joins CEL expressions with &&. It skips falsy values (null, undefined, empty string). So if filter is null:
AND(EQ('data.metadata.name', name), null)just returnsEQ('data.metadata.name', name)- It behaves exactly like the original code before the change:
filter: EQ('data.metadata.name', name)
There was a problem hiding this comment.
what is the expected format for the
filter
It's a CEL string for the Tekton Results API. The format looks like:
data.status.startTime > timestamp("2026-06-22T10:00:00.000Z")
This is generated by useDateRangeFilter in dateFilterCEL. It's the same format used everywhere else in the codebase for Tekton Results queries (e.g., EQ('data.metadata.name', name) produces data.metadata.name == "xyz").
There was a problem hiding this comment.
please add verification
| () => ({ | ||
| ...(selector && { selector }), | ||
| ...(options?.filter && { filter: options.filter }), | ||
| }), |
There was a problem hiding this comment.
can you break this down to if-else so that it is easier to read ?
Also describe how the flow happens from pipelinerun list page to useTaskRuns.ts - try and think about the edge cases, also how it might change in future for taskruns
There was a problem hiding this comment.
Changed it to "if-else". Also I am creating a doc for explaining the flow, edge cases and future change.
| const isTektonResultEnabled = useFlag(FLAG_PIPELINE_TEKTON_RESULT_INSTALLED); | ||
| const [timespan, setTimespan, preferenceLoaded] = useUserPreference<number>( | ||
| SETTINGS_KEY, | ||
| parsePrometheusDuration('1d'), |
There was a problem hiding this comment.
are we setting 1d as default ?
There was a problem hiding this comment.
are we setting 1d as default ?
Yes
| const currentKey = formatPrometheusDuration(timespan); | ||
| const timeRangeOptions = isTektonResultEnabled | ||
| ? TimeRangeOptions() | ||
| : TimeRangeOptionsK8s(); |
There was a problem hiding this comment.
I do not think we need two separate options here as k8 anyway does not support server side rendering, the one in pipeline overview is added because of prometheus
@arvindk-softwaredev wdyt ?
There was a problem hiding this comment.
Removed TimeRangeOptionsK8
| ? TimeRangeOptions() | ||
| : TimeRangeOptionsK8s(); | ||
|
|
||
| const filterValues = useMemo( |
There was a problem hiding this comment.
how much are we really saving in terms of performance with this useMemo ?
There was a problem hiding this comment.
how much are we really saving in terms of performance with this useMemo ?
Without useMemo, React creates a new filterValues object on every render, even if nothing changed. DataViewFilterToolbar sees a "new" object each time and re-renders unnecessarily.
With useMemo, React reuses the same object until baseFilterValues or currentKey actually changes. This avoids those extra re-renders. Removing it wouldn't cause any visible performance issue as the object is small.
There was a problem hiding this comment.
please explain how would this change if the values in dependency arrays were props
There was a problem hiding this comment.
I think it will work same because useMemo only compares current values with the previous one. If the parent passes new object every render then it will recompute again and again.
There was a problem hiding this comment.
I think it will work same because useMemo only compares current values with the previous one. If the parent passes new object every render then it will recompute again and again.
exactly, therefore is there a benefit ?
There was a problem hiding this comment.
Got it. I have removed useMemo.
There was a problem hiding this comment.
but in this case were the dependencies props ?
There was a problem hiding this comment.
No, they are not
| return ( | ||
| <ListPageBody> | ||
| {!hideTextFilter && ( | ||
| {!hideTextFilter && preferenceLoaded && ( |
There was a problem hiding this comment.
what happens when preferenceLoaded is false ? the user sees nothing in this implementation, we should add necessary error banners / loaders
There was a problem hiding this comment.
I will try this case practically and update
There was a problem hiding this comment.
bubble the error state and then try please
There was a problem hiding this comment.
|
|
||
| export const useDateRangeFilter = (): DateRangeFilterResult => { | ||
| const isTektonResultEnabled = useFlag(FLAG_PIPELINE_TEKTON_RESULT_INSTALLED); | ||
| const [timespan, setTimespan, preferenceLoaded] = useUserPreference<number>( |
There was a problem hiding this comment.
what if this api fails ?
can we bubble the error please
There was a problem hiding this comment.
what if this api fails ? can we bubble the error please
If this api fails, then it falls back to the default value which is "Last day". This will not throw an error as it is defined in SDK
There was a problem hiding this comment.
suppressing the error makes it harder to debug when an issue comes in later, so we will need to change this
There was a problem hiding this comment.
console/frontend/packages/console-shared/src/hooks/useUserPreference.ts
they are handling the error in someways, let's check this once and see if we can understand if there's an api error
There was a problem hiding this comment.
Also adding this in the doc in detail.
| useUserPreference, | ||
| } from '@openshift-console/dynamic-plugin-sdk'; | ||
| import { FLAG_PIPELINE_TEKTON_RESULT_INSTALLED } from '../../consts'; | ||
| import { parsePrometheusDuration } from '../pipelines-overview/dateTime'; |
There was a problem hiding this comment.
why are we using this parsePrometheusDuration ?
we are going to query tektonresults and this is very unreadable
try { const parts = duration .trim() .split(/\s+/) .map((p) => p.match(/^(\d+)([wdhms])$/)); return _.sumBy(parts, (p) => parseInt(p[1], 10) * units[p[2]]); } catch (ignored) { // Invalid duration format return 0; }
There was a problem hiding this comment.
why are we using this
parsePrometheusDuration? we are going to query tektonresults and this is very unreadabletry { const parts = duration .trim() .split(/\s+/) .map((p) => p.match(/^(\d+)([wdhms])$/)); return _.sumBy(parts, (p) => parseInt(p[1], 10) * units[p[2]]); } catch (ignored) { // Invalid duration format return 0; }
parsePrometheusDuration simply converts a human-readable duration string like '1d' into milliseconds (86400000). It's not related to Prometheus or Tekton Results, it's just a utility for parsing duration strings. We use it because:
- The time range dropdown options are stored as duration keys ('1d', '1w', '1m', etc.)
- We need milliseconds for
Date.now() - timespancalculation - The Overview page already uses this same function for the same purpose
- It's already in the codebase, so no new dependency
The name is misleading (it's borrowed from Prometheus-style duration format), but the function itself is just string to milliseconds conversion.
There was a problem hiding this comment.
@adityavshinde
This is a request
We do not want AI responses to review comment, write based on what you understand and can verify else there will be no learning from this
cc : @arvindk-softwaredev
There was a problem hiding this comment.
Should I import the unit constants directly from dateTime.ts, then further do the calculation for the startDate?
There was a problem hiding this comment.
you can create your own helper method which is more readable and maintainable
There was a problem hiding this comment.
We need milliseconds for Date.now() - timespan calculation
We should update this probably, if we do this what essentially happens is it does not compute the whole of the earlier day, example
2 PM on June 23rd - and timespan = 1 day = it will compute till 2 PM of June 22nd, it won't share the whole data for June 22nd, this can misconstrue things. For this filter we should have all data rom now to 00:00 and then add the timespan calculation so that it is normalised.
wdyt @arvindk-softwaredev ?
There was a problem hiding this comment.
Changed it accordingly
| isExpanded={isOpen} | ||
| isFullWidth | ||
| icon={<FilterIcon />} | ||
| icon={config.singleSelect ? undefined : <FilterIcon />} |
There was a problem hiding this comment.
why do we need to pass undefined for the icon?
There was a problem hiding this comment.
Reverted this back to the filter icon which is already in use.
|
|
||
| export const useDateRangeFilter = (): DateRangeFilterResult => { | ||
| const isTektonResultEnabled = useFlag(FLAG_PIPELINE_TEKTON_RESULT_INSTALLED); | ||
| const [timespan, setTimespan, preferenceLoaded] = useUserPreference<number>( |
There was a problem hiding this comment.
update setTimespan to something more specific
There was a problem hiding this comment.
Updated setTimespan to setTimespanDateRange
There was a problem hiding this comment.
I still see this as setTimespan 🤔
There was a problem hiding this comment.
Now, we have completely replaced it with setTimespanDateFilter.
fd5de3a to
b496226
Compare
f066d53 to
dba93db
Compare
|
|
||
| const DURATION_MS: Record<string, number> = { | ||
| s: 1000, | ||
| m: 60000, |
There was a problem hiding this comment.
nit: wherever you are using this can you please break this down numerically ? it is easier to debug
example:
s= 1000,
m= 60*s,
something like that
There was a problem hiding this comment.
Okay, i have made the changes accordingly for better debugging.
| options: Object.entries(timeRangeOptions).map(([key, label]) => ({ | ||
| value: key, | ||
| label, | ||
| count: 0, |
There was a problem hiding this comment.
what is 0 here ?
There was a problem hiding this comment.
count is a mandatory field for filter options, since I am reusing the checkbox filter. But in this case we don't need a count for radio dropdown also singleSelect: true hides the count. So 0 is just a placeholder here. (just to satisfy the type)
There was a problem hiding this comment.
It is not a good practice to use magic numbers
Could we avoid using a magic number here by introducing a named constant for this value ?
There was a problem hiding this comment.
constant name COUNT_ZERO would be fine? or shall I add a comment explaining why?
There was a problem hiding this comment.
🤔 it should be something that explains what the value represents
| const [timespan, setTimespanDateFilter, preferenceLoaded] = | ||
| useUserPreference<number>( | ||
| `${USER_PREFERENCE_PREFIX}.dateRangeFilter.${pageType}`, | ||
| 0, |
There was a problem hiding this comment.
@adityavshinde can you update this number 0 with what it actually represents ?
There was a problem hiding this comment.
Updated it to NO_DATE_RANGE_FILTER
| true, | ||
| ); | ||
|
|
||
| const ts = timespan ?? 0; |
There was a problem hiding this comment.
update the name ts to something meaningful
There was a problem hiding this comment.
Renamed it to selectedTimespan
| filter: dateFilterCEL, | ||
| }); | ||
|
|
||
| const dateFilteredRuns = useMemo(() => { |
There was a problem hiding this comment.
this should be moved to useDataViewFilter and this should check whether there's pre selected dataSource as we do not need this for tektonresults data
There was a problem hiding this comment.
Moved this to useDataViewFilter, also added the check for tekton results.
| filterValues, | ||
| onFilterChange, | ||
| onClearAll, | ||
| filterValues: baseFilterValues, |
There was a problem hiding this comment.
This needs to be moved inside console-plugin/src/components/hooks/useDataViewFilter.ts.
What you are doing here is mutating methods that the hook exposes using baseFilterValues, baseOnFilterChange and baseOnClearAll which is not a practice that should be followed.
f0e5d25 to
66a700e
Compare
Signed-off-by: Aditya Shinde <adishind@redhat.com>
66a700e to
801adb4
Compare


Summary
Adds a time range filter to the PipelineRuns list page, allowing users to filter pipeline runs
by time period (Last day, Last week, Last month, Last quarter, Last year).
useUserPreference(stored in ConfigMap per user)singleSelectsupport toCheckboxFilterConfigfor reuse by other filtersChanged files
useDateRangeFilter.ts(new) - Hook managing time range state, persistence, and CEL generationuseTektonResult.ts- Accepts and forwardsfilterparameter to downstream hooksuseTaskRuns.ts- Threadsfilterto Tekton Results API, combines with name filter viaAND()DataViewFilterToolbar.tsx- AddedsingleSelectmode toCheckboxFilterInputPipelineRunsList.tsx- Integrates time range into the filter systemScreen Recording
RFE-7628.mp4
Test plan
startTime(pending) are preserved