From 09a6e7ca9a3eb5ee7bd17d38ff06545b117a21a0 Mon Sep 17 00:00:00 2001 From: Vikram Raj Date: Wed, 13 Dec 2023 09:03:32 +0530 Subject: [PATCH] Integrate metrics tab for Pipeline and Repository with Summary API fix Average duration chart --- console-extensions.json | 18 +++ .../en/plugin__pipeline-console-plugin.json | 2 - .../PipelinesAverageDuration.tsx | 62 ++++++++++- .../pipelines-metrics/PipelinesMetrics.scss | 5 +- .../PipelinesMetricsPage.tsx | 38 +++---- .../PipelineRunsDurationCard.tsx | 10 +- .../PipelineRunsNumbersChart.tsx | 12 +- .../PipelineRunsStatusCard.tsx | 10 +- .../PipelinesOverviewPage.tsx | 7 +- src/components/pipelines-overview/dateTime.ts | 46 ++++++-- src/components/pipelines-overview/utils.ts | 103 ++++++++++++------ 11 files changed, 226 insertions(+), 87 deletions(-) diff --git a/console-extensions.json b/console-extensions.json index fbe3e7de..8c5dc19b 100644 --- a/console-extensions.json +++ b/console-extensions.json @@ -77,6 +77,24 @@ "required": ["PIPELINE_TEKTON_RESULT_INSTALLED"] } }, + { + "type": "console.tab/horizontalNav", + "properties": { + "model": { + "group": "pipelinesascode.tekton.dev", + "version": "v1alpha1", + "kind": "Repository" + }, + "page": { + "name": "%Metrics%", + "href": "metrics" + }, + "component": { "$codeRef": "metricsComponent.PipelinesMetricsPage" } + }, + "flags": { + "required": ["PIPELINE_TEKTON_RESULT_INSTALLED"] + } + }, { "type": "console.flag/hookProvider", "properties": { diff --git a/locales/en/plugin__pipeline-console-plugin.json b/locales/en/plugin__pipeline-console-plugin.json index 491613ff..939fc508 100644 --- a/locales/en/plugin__pipeline-console-plugin.json +++ b/locales/en/plugin__pipeline-console-plugin.json @@ -13,7 +13,6 @@ "3 days": "3 days", "3 weeks": "3 weeks", "4 weeks": "4 weeks", - "Administrators can try this quick start to configure their metrics level to pipelinerun and taskrun. The pipelinerun and taskrun metrics level collects large volume of metrics over time in unbounded cardinality which may lead to metrics unreliability.": "Administrators can try this quick start to configure their metrics level to pipelinerun and taskrun. The pipelinerun and taskrun metrics level collects large volume of metrics over time in unbounded cardinality which may lead to metrics unreliability.", "All": "All", "Average duration": "Average duration", "Average Duration": "Average Duration", @@ -32,7 +31,6 @@ "Per Pipeline": "Per Pipeline", "Per Repository": "Per Repository", "Pipeline": "Pipeline", - "Pipeline metrics configuration defaults to pipelines and task level": "Pipeline metrics configuration defaults to pipelines and task level", "PipelineRun status": "PipelineRun status", "Pipelines": "Pipelines", "Project": "Project", diff --git a/src/components/pipelines-metrics/PipelinesAverageDuration.tsx b/src/components/pipelines-metrics/PipelinesAverageDuration.tsx index f77331a0..f9c73407 100644 --- a/src/components/pipelines-metrics/PipelinesAverageDuration.tsx +++ b/src/components/pipelines-metrics/PipelinesAverageDuration.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import * as _ from 'lodash'; +import _ from 'lodash'; import * as classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { DomainPropType, DomainTuple } from 'victory-core'; @@ -15,14 +15,24 @@ import { import { Card, CardBody, CardTitle } from '@patternfly/react-core'; import { formatDate, + getDropDownDate, getXaxisValues, parsePrometheusDuration, + timeToMinutes, } from '../pipelines-overview/dateTime'; +import { ALL_NAMESPACES_KEY } from '../../consts'; +import { DataType } from '../utils/tekton-results'; +import { SummaryResponse, getResultsSummary } from '../utils/summary-api'; +import { getFilter, useInterval } from '../pipelines-overview/utils'; interface PipelinesAverageDurationProps { timespan?: number; domain?: DomainPropType; bordered?: boolean; + interval?: number; + parentName?: string; + kind?: string; + namespace?: string; } type DomainType = { x?: DomainTuple; y?: DomainTuple }; @@ -30,8 +40,13 @@ const PipelinesAverageDuration: React.FC = ({ timespan, domain, bordered, + interval, + parentName, + namespace, + kind, }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); + const [data, setData] = React.useState(); const startTimespan = timespan - parsePrometheusDuration('1d'); const endDate = new Date(Date.now()).setHours(0, 0, 0, 0); const startDate = new Date(Date.now() - startTimespan).setHours(0, 0, 0, 0); @@ -40,10 +55,45 @@ const PipelinesAverageDuration: React.FC = ({ x: domainX || [startDate, endDate], y: domainY || undefined, }; + + if (namespace == ALL_NAMESPACES_KEY) { + namespace = '-'; + } + const tickValues = getXaxisValues(timespan); + const date = getDropDownDate(timespan).toISOString(); + + const getSummaryData = () => { + const summaryOpt = { + summary: 'avg_duration', + data_type: DataType.PipelineRun, + filter: getFilter(date, parentName, kind), + groupBy: 'day', + }; + + getResultsSummary(namespace, summaryOpt) + .then((d) => { + setData(d); + }) + .catch((e) => { + throw e; + }); + }; + + useInterval(getSummaryData, interval, namespace, date); + const chartData = tickValues.map((value) => { - return { x: value, y: 100 }; + const s = data?.summary.find((d) => { + return ( + new Date(d.group_value * 1000).toDateString() === + new Date(value).toDateString() + ); + }); + return { + x: value, + y: timeToMinutes(s?.avg_duration) || 0, + }; }); if (!domainY) { @@ -94,7 +144,7 @@ const PipelinesAverageDuration: React.FC = ({ `${datum.y}`} + labels={({ datum }) => `${datum.y}m`} constrainToVisibleArea /> } @@ -115,7 +165,11 @@ const PipelinesAverageDuration: React.FC = ({ style={xAxisStyle} tickFormat={xTickFormat} /> - + `${v}m`} + /> diff --git a/src/components/pipelines-metrics/PipelinesMetrics.scss b/src/components/pipelines-metrics/PipelinesMetrics.scss index 7cce8bb7..ff10c547 100644 --- a/src/components/pipelines-metrics/PipelinesMetrics.scss +++ b/src/components/pipelines-metrics/PipelinesMetrics.scss @@ -1,5 +1,6 @@ -.pipeline-metrics__alert { - margin: var(--pf-global--spacer--md); +.pipelines-metrix-dropdown { + margin-top: var(--pf-global--spacer--md); + margin-left: var(--pf-global--spacer--md); } .pipelines-metrics__background { diff --git a/src/components/pipelines-metrics/PipelinesMetricsPage.tsx b/src/components/pipelines-metrics/PipelinesMetricsPage.tsx index f3353167..b1a6cece 100644 --- a/src/components/pipelines-metrics/PipelinesMetricsPage.tsx +++ b/src/components/pipelines-metrics/PipelinesMetricsPage.tsx @@ -1,18 +1,21 @@ import * as React from 'react'; import { useParams } from 'react-router-dom-v5-compat'; -import { useTranslation } from 'react-i18next'; import PipelineRunsStatusCard from '../pipelines-overview/PipelineRunsStatusCard'; -import { Alert, Flex, FlexItem } from '@patternfly/react-core'; +import { Flex, FlexItem } from '@patternfly/react-core'; import PipelinesRunsDurationCard from '../pipelines-overview/PipelineRunsDurationCard'; import PipelinesRunsNumbersChart from '../pipelines-overview/PipelineRunsNumbersChart'; import { parsePrometheusDuration } from '../pipelines-overview/dateTime'; import TimeRangeDropdown from '../pipelines-overview/TimeRangeDropdown'; import RefreshDropdown from '../pipelines-overview/RefreshDropdown'; import PipelinesAverageDuration from './PipelinesAverageDuration'; +import { K8sResourceKind } from '@openshift-console/dynamic-plugin-sdk'; import './PipelinesMetrics.scss'; -const PipelinesMetricsPage: React.FC = () => { - const { t } = useTranslation('plugin__pipeline-console-plugin'); +type PipelinesMetricsPageProps = { + obj: K8sResourceKind; +}; + +const PipelinesMetricsPage: React.FC = ({ obj }) => { const params = useParams(); const { ns: namespace, name: parentName } = params; const [timespan, setTimespan] = React.useState(parsePrometheusDuration('1w')); @@ -22,21 +25,7 @@ const PipelinesMetricsPage: React.FC = () => { return ( <> - -

- {t( - 'Administrators can try this quick start to configure their metrics level to pipelinerun and taskrun. The pipelinerun and taskrun metrics level collects large volume of metrics over time in unbounded cardinality which may lead to metrics unreliability.', - )} -

-
- + @@ -51,6 +40,8 @@ const PipelinesMetricsPage: React.FC = () => { domain={{ y: [0, 100] }} bordered={false} namespace={namespace} + parentName={parentName} + kind={obj.kind} interval={interval} /> @@ -63,6 +54,8 @@ const PipelinesMetricsPage: React.FC = () => { namespace={namespace} parentName={parentName} timespan={timespan} + interval={interval} + kind={obj.kind} domain={{ y: [0, 500] }} /> @@ -72,7 +65,11 @@ const PipelinesMetricsPage: React.FC = () => { > { parentName={parentName} timespan={timespan} interval={interval} + kind={obj.kind} /> diff --git a/src/components/pipelines-overview/PipelineRunsDurationCard.tsx b/src/components/pipelines-overview/PipelineRunsDurationCard.tsx index fdbaa950..1898ef3d 100644 --- a/src/components/pipelines-overview/PipelineRunsDurationCard.tsx +++ b/src/components/pipelines-overview/PipelineRunsDurationCard.tsx @@ -14,7 +14,7 @@ import { Grid, GridItem, } from '@patternfly/react-core'; -import { SummaryProps, useInterval } from './utils'; +import { SummaryProps, getFilter, useInterval } from './utils'; import { getResultsSummary } from '../utils/summary-api'; import { DataType } from '../utils/tekton-results'; import { ALL_NAMESPACES_KEY } from '../../consts'; @@ -30,6 +30,7 @@ interface PipelinesRunsDurationProps { parentName?: string; summaryData?: SummaryProps; bordered?: boolean; + kind?: string; } const PipelinesRunsDurationCard: React.FC = ({ @@ -38,6 +39,7 @@ const PipelinesRunsDurationCard: React.FC = ({ parentName, interval, bordered, + kind, }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); const [summaryData, setSummaryData] = React.useState({}); @@ -51,16 +53,12 @@ const PipelinesRunsDurationCard: React.FC = ({ }, [namespace, timespan]); const date = getDropDownDate(timespan).toISOString(); - let filter = ''; - parentName - ? (filter = `data.status.startTime>timestamp("${date}")&&data.metadata.labels['tekton.dev/pipeline']=="${parentName}"`) - : (filter = `data.status.startTime>timestamp("${date}")`); const getSummaryData = () => { getResultsSummary(namespace, { summary: 'total_duration,avg_duration,max_duration', data_type: DataType.PipelineRun, - filter, + filter: getFilter(date, parentName, kind), }) .then((response) => { setLoaded(true); diff --git a/src/components/pipelines-overview/PipelineRunsNumbersChart.tsx b/src/components/pipelines-overview/PipelineRunsNumbersChart.tsx index 0387b436..6214c19d 100644 --- a/src/components/pipelines-overview/PipelineRunsNumbersChart.tsx +++ b/src/components/pipelines-overview/PipelineRunsNumbersChart.tsx @@ -22,7 +22,7 @@ import { import { DataType } from '../utils/tekton-results'; import { SummaryResponse, getResultsSummary } from '../utils/summary-api'; import { ALL_NAMESPACES_KEY } from '../../consts'; -import { useInterval } from './utils'; +import { getFilter, useInterval } from './utils'; import { LoadingInline } from '../Loading'; interface PipelinesRunsNumbersChartProps { @@ -32,6 +32,7 @@ interface PipelinesRunsNumbersChartProps { domain?: DomainPropType; parentName?: string; bordered?: boolean; + kind?: string; } type DomainType = { x?: DomainTuple; y?: DomainTuple }; @@ -42,6 +43,7 @@ const PipelinesRunsNumbersChart: React.FC = ({ domain, parentName, bordered, + kind, }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); const startTimespan = timespan - parsePrometheusDuration('1d'); @@ -59,20 +61,16 @@ const PipelinesRunsNumbersChart: React.FC = ({ if (namespace == ALL_NAMESPACES_KEY) { namespace = '-'; } + const tickValues = getXaxisValues(timespan); const date = getDropDownDate(timespan).toISOString(); - let filter = ''; - parentName - ? (filter = `data.status.startTime>timestamp("${date}")&&data.metadata.labels['tekton.dev/pipeline']=="${parentName}"`) - : (filter = `data.status.startTime>timestamp("${date}")`); - const getSummaryData = () => { const summaryOpt = { summary: 'total', data_type: DataType.PipelineRun, - filter, + filter: getFilter(date, parentName, kind), groupBy: 'day', }; diff --git a/src/components/pipelines-overview/PipelineRunsStatusCard.tsx b/src/components/pipelines-overview/PipelineRunsStatusCard.tsx index 20cdd14e..774cc18f 100644 --- a/src/components/pipelines-overview/PipelineRunsStatusCard.tsx +++ b/src/components/pipelines-overview/PipelineRunsStatusCard.tsx @@ -35,7 +35,7 @@ import { getXaxisValues, parsePrometheusDuration, } from './dateTime'; -import { useInterval } from './utils'; +import { getFilter, useInterval } from './utils'; import { SummaryResponse, getResultsSummary } from '../utils/summary-api'; import { DataType } from '../utils/tekton-results'; import './PipelinesOverview.scss'; @@ -48,6 +48,8 @@ interface PipelinesRunsStatusCardProps { bordered?: boolean; namespace: string; interval: number; + kind?: string; + parentName?: string; } type DomainType = { x?: DomainTuple; y?: DomainTuple }; @@ -58,6 +60,8 @@ const PipelinesRunsStatusCard: React.FC = ({ bordered, namespace, interval, + parentName, + kind, }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); const [data, setData] = React.useState(); @@ -80,7 +84,7 @@ const PipelinesRunsStatusCard: React.FC = ({ const getSummaryData = (summary, setData, groupBy?) => { const summaryOpt = { summary, - filter: `data.status.startTime>timestamp("${date}")`, + filter: getFilter(date, parentName, kind), data_type: DataType.PipelineRun, }; groupBy && (summaryOpt['groupBy'] = groupBy); @@ -300,7 +304,7 @@ const PipelinesRunsStatusCard: React.FC = ({ } scale={{ x: 'time', y: 'linear' }} domain={domainValue} - domainPadding={{ x: [30, 25], y: [30, 25] }} + domainPadding={{ x: [30, 25] }} height={200} padding={{ top: 20, diff --git a/src/components/pipelines-overview/PipelinesOverviewPage.tsx b/src/components/pipelines-overview/PipelinesOverviewPage.tsx index 025d591e..9625fcc7 100644 --- a/src/components/pipelines-overview/PipelinesOverviewPage.tsx +++ b/src/components/pipelines-overview/PipelinesOverviewPage.tsx @@ -26,7 +26,6 @@ const PipelinesOverviewPage: React.FC = () => { setActiveNamespace(namespace); }, [namespace]); - return ( <>
@@ -92,7 +91,11 @@ const PipelinesOverviewPage: React.FC = () => {
- +
); diff --git a/src/components/pipelines-overview/dateTime.ts b/src/components/pipelines-overview/dateTime.ts index a9cdc0c2..21e1170d 100644 --- a/src/components/pipelines-overview/dateTime.ts +++ b/src/components/pipelines-overview/dateTime.ts @@ -98,29 +98,34 @@ export const formatTime = (time: string): string => { }; export const formatTimeLastRunTime = (time: string): string => { - let timeValue = time?.split(/\s+/); + const timeValue = time?.split(/\s+/); if (timeValue === undefined) { return '-'; } - if(timeValue.length > 1){ + if (timeValue.length > 1) { // Check for the presence of each component const hasYear = timeValue.includes('year') || timeValue.includes('years'); - const hasMonth = timeValue.includes('month') || timeValue.includes('months'); + const hasMonth = + timeValue.includes('month') || timeValue.includes('months'); const hasDay = timeValue.includes('day') || timeValue.includes('days'); // Return the most significant time component if (hasYear) { - return timeValue.includes('year') ? timeValue[timeValue.indexOf('year') - 1] + ' year ago' : timeValue[timeValue.indexOf('years') - 1] + ' years ago'; + return timeValue.includes('year') + ? timeValue[timeValue.indexOf('year') - 1] + ' year ago' + : timeValue[timeValue.indexOf('years') - 1] + ' years ago'; } else if (hasMonth) { - return timeValue.includes('month') ? timeValue[timeValue.indexOf('month') - 1] + ' month ago' : timeValue[timeValue.indexOf('months') - 1] + ' months ago'; + return timeValue.includes('month') + ? timeValue[timeValue.indexOf('month') - 1] + ' month ago' + : timeValue[timeValue.indexOf('months') - 1] + ' months ago'; } else if (hasDay) { - return timeValue.includes('day') ? timeValue[timeValue.indexOf('day') - 1] + ' day ago' : timeValue[timeValue.indexOf('days') - 1] + ' days ago'; - } - else { + return timeValue.includes('day') + ? timeValue[timeValue.indexOf('day') - 1] + ' day ago' + : timeValue[timeValue.indexOf('days') - 1] + ' days ago'; + } else { return `${timeValue.pop()} ago`; } - } - else{ + } else { const [hours, minutes, seconds] = time.split(/[:.]/).map(Number); if (!isNaN(hours) && hours > 0) { return hours === 1 ? `${hours} hour ago` : `${hours} hours ago`; @@ -145,3 +150,24 @@ export const dateFormatterNoYear = new Intl.DateTimeFormat( export const formatDate = (date: Date) => { return dateFormatterNoYear.format(date); }; + +export const timeToMinutes = (timeString: string): number => { + // Parse the time string + const match = timeString?.split(/[:]+/); + + if (match) { + // Extract components + const hours = parseInt(match[0]); + const minutes = parseInt(match[1]); + const seconds = parseInt(match[2]); + + // Calculate total minutes + const totalMinutes = hours * 60 + minutes + seconds / 60; + + return totalMinutes; + } else { + // Handle invalid time string + console.error('Invalid time format'); + return null; + } +}; diff --git a/src/components/pipelines-overview/utils.ts b/src/components/pipelines-overview/utils.ts index 3028d86e..98c1c54b 100644 --- a/src/components/pipelines-overview/utils.ts +++ b/src/components/pipelines-overview/utils.ts @@ -1,6 +1,10 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import { K8sGroupVersionKind, K8sModel, K8sResourceKindReference } from '@openshift-console/dynamic-plugin-sdk'; +import { + K8sGroupVersionKind, + K8sModel, + K8sResourceKindReference, +} from '@openshift-console/dynamic-plugin-sdk'; export const alphanumericCompare = (a: string, b: string): number => { return a.localeCompare(b, undefined, { @@ -15,7 +19,6 @@ export type SummaryProps = { succeeded?: number; failed?: number; running?: number; - unknown?: number; cancelled?: number; max_duration?: string; min_duration?: string; @@ -39,7 +42,7 @@ export const listPageTableColumnClasses = [ 'pf-m-hidden pf-m-visible-on-md', //total duration 'pf-m-hidden pf-m-visible-on-xl', //avg duration 'pf-m-hidden pf-m-visible-on-xl', //success rate - 'pf-m-hidden pf-m-visible-on-xl' //last run time + 'pf-m-hidden pf-m-visible-on-xl', //last run time ]; export const TimeRangeOptions = () => { @@ -80,35 +83,55 @@ export const LAST_LANGUAGE_LOCAL_STORAGE_KEY = 'bridge/last-language'; export const getLastLanguage = (): string => localStorage.getItem(LAST_LANGUAGE_LOCAL_STORAGE_KEY); +export const getReferenceForModel = ( + model: K8sModel, +): K8sResourceKindReference => + getReference({ + group: model.apiGroup, + version: model.apiVersion, + kind: model.kind, + }); -export const getReferenceForModel = (model: K8sModel): K8sResourceKindReference => - getReference({ group: model.apiGroup, version: model.apiVersion, kind: model.kind }); - export const getReference = ({ - group, - version, - kind, -}: K8sGroupVersionKind): K8sResourceKindReference => [group || 'core', version, kind].join('~'); - -export const sortByProperty = (array: SummaryProps[], prop: string, direction: string) => { - return array.sort((a:SummaryProps, b:SummaryProps) => { - const nameA = prop === 'namespace' ? a.group_value.split('/')[0].toLowerCase() : a.group_value.split('/')[1].toLowerCase(); - const nameB = prop === 'namespace' ? b.group_value.split('/')[0].toLowerCase() : b.group_value.split('/')[1].toLowerCase(); - - const numberA = parseInt(nameA.replace(/^\D+/g, '')) || 0; - const numberB = parseInt(nameB.replace(/^\D+/g, '')) || 0; - - const nameComparison = nameA.localeCompare(nameB); - - if (nameComparison === 0) { - return (direction === 'desc' ? numberB - numberA : numberA - numberB); - } - - return (direction === 'desc' ? nameComparison * -1 : nameComparison); - }) + group, + version, + kind, +}: K8sGroupVersionKind): K8sResourceKindReference => + [group || 'core', version, kind].join('~'); + +export const sortByProperty = ( + array: SummaryProps[], + prop: string, + direction: string, +) => { + return array.sort((a: SummaryProps, b: SummaryProps) => { + const nameA = + prop === 'namespace' + ? a.group_value.split('/')[0].toLowerCase() + : a.group_value.split('/')[1].toLowerCase(); + const nameB = + prop === 'namespace' + ? b.group_value.split('/')[0].toLowerCase() + : b.group_value.split('/')[1].toLowerCase(); + + const numberA = parseInt(nameA.replace(/^\D+/g, '')) || 0; + const numberB = parseInt(nameB.replace(/^\D+/g, '')) || 0; + + const nameComparison = nameA.localeCompare(nameB); + + if (nameComparison === 0) { + return direction === 'desc' ? numberB - numberA : numberA - numberB; + } + + return direction === 'desc' ? nameComparison * -1 : nameComparison; + }); }; -export const sortTimeStrings = (array: SummaryProps[], prop: string ,direction: string) => { +export const sortTimeStrings = ( + array: SummaryProps[], + prop: string, + direction: string, +) => { return array.slice().sort((a, b) => { const getTimeValue = (timeString) => { const components = timeString?.split(/\s+/); @@ -126,7 +149,8 @@ export const sortTimeStrings = (array: SummaryProps[], prop: string ,direction: } else if (component.includes(':')) { // Parse HH:mm:ss.ms const timeParts = component.split(':').map(Number); - totalSeconds += timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]; + totalSeconds += + timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]; if (timeParts.length === 4) { totalSeconds += timeParts[3] / 1000; } @@ -144,7 +168,11 @@ export const sortTimeStrings = (array: SummaryProps[], prop: string ,direction: }); }; -export const sortByNumbers = (array: SummaryProps[], prop: string ,direction: string) => { +export const sortByNumbers = ( + array: SummaryProps[], + prop: string, + direction: string, +) => { const modifier = direction === 'desc' ? -1 : 1; return array.slice().sort((a, b) => { @@ -165,7 +193,7 @@ export const useInterval = ( interval: number, namespace: string, date: string, - pageFlag?: number + pageFlag?: number, ) => { React.useEffect(() => { getData(); @@ -176,3 +204,16 @@ export const useInterval = ( }, [interval, namespace, date, pageFlag]); }; +export const getFilter = (date, parentName, kind): string => { + let filter = `data.status.startTime>timestamp("${date}")`; + if (parentName) { + if (kind === 'Repository') { + filter = `data.status.startTime>timestamp("${date}")&&data.metadata.labels['pipelinesascode.tekton.dev/repository']=="${parentName}"`; + } else { + filter = `data.status.startTime>timestamp("${date}")&&!data.metadata.labels.contains('pipelinesascode.tekton.dev/repository')&&data.metadata.labels['tekton.dev/pipeline']=="${parentName}"`; + } + } else { + filter = `data.status.startTime>timestamp("${date}")`; + } + return filter; +};