Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions console-extensions.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
[
{
"type": "console.redux-reducer",
"properties": {
"scope": "pipelinesOverviewFilters",
"reducer": {
"$codeRef": "pipelinesOverviewFiltersReducer.pipelinesOverviewFiltersReducer"
}
}
},
{
"type": "console.flag/model",
"properties": {
Expand Down
2 changes: 2 additions & 0 deletions locales/en/plugin__pipelines-console-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@
"Events triggering the webhook: ": "Events triggering the webhook: ",
"Expand": "Expand",
"Failed": "Failed",
"Failed to load average duration data": "Failed to load average duration data",
"Failed to load duration data": "Failed to load duration data",
"Failed to load pipeline run status data": "Failed to load pipeline run status data",
"Failed to load pipeline runs list": "Failed to load pipeline runs list",
Expand Down Expand Up @@ -584,6 +585,7 @@
"Try it": "Try it",
"Unable to detect Event listener URL": "Unable to detect Event listener URL",
"Unable to find repository": "Unable to find repository",
"Unable to load average duration": "Unable to load average duration",
"Unable to load duration": "Unable to load duration",
"Unable to load Pipeline": "Unable to load Pipeline",
"Unable to load pipeline runs": "Unable to load pipeline runs",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@
"taskDetails": "./components/tasks",
"pacComponent": "./components/pac/",
"yamlTemplates": "./components/templates",
"topology": "./components/topology"
"topology": "./components/topology",
"pipelinesOverviewFiltersReducer": "./redux/reducers/pipelines-overview-filters"
},
"dependencies": {
"@console/pluginAPI": ">=4.15"
Expand Down
111 changes: 111 additions & 0 deletions src/components/hooks/usePersistedFiltersForPipelineOverview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: FIXME missing exports due to out-of-sync @types/react-redux version
import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom-v5-compat';
import {
setInterval as setIntervalAction,
setTimespan as setTimespanAction,
} from '../../redux/actions/pipelines-overview-filters';
import {
getPipelinesOverviewInterval,
getPipelinesOverviewTimespan,
} from '../../redux/selectors/pipelines-overview-filters';
import { useQueryParams } from '../pipelines-overview/utils';

export const usePersistedTimespanWithUrl = (
defaultValue: number,
options: {
displayFormat: (v: number) => string;
loadFormat: (v: string) => number;
options: Record<string, any>;
},
namespace: string,
) => {
const dispatch = useDispatch();
const reduxTimespan = useSelector(getPipelinesOverviewTimespan);
const location = useLocation();
const prevNamespaceRef = React.useRef(namespace);
const [timespan, setTimespanValue] = React.useState(() => {
const urlParams = new URLSearchParams(location.search);
const urlValue = urlParams.has('timerange')
? urlParams.get('timerange')
: null;
if (urlValue) {
return options.loadFormat(urlValue);
}
return reduxTimespan ?? defaultValue;
});

// Reset to default when namespace changes
React.useEffect(() => {
if (prevNamespaceRef.current !== namespace) {
prevNamespaceRef.current = namespace;
setTimespanValue(defaultValue);
}
}, [namespace, defaultValue]);

// Persist to Redux whenever value changes
React.useEffect(() => {
dispatch(setTimespanAction(timespan));
}, [timespan, dispatch]);

useQueryParams({
key: 'timerange',
value: timespan,
setValue: setTimespanValue,
defaultValue: timespan,
...options,
});

return [timespan, setTimespanValue];
};

export const usePersistedIntervalWithUrl = (
defaultValue: number,
options: {
displayFormat: (v: number | null) => string;
loadFormat: (v: string) => number | null;
options: Record<string, any>;
},
namespace: string,
) => {
const dispatch = useDispatch();
const reduxInterval = useSelector(getPipelinesOverviewInterval);
const location = useLocation();
const prevNamespaceRef = React.useRef(namespace);
const [interval, setIntervalValue] = React.useState(() => {
const urlParams = new URLSearchParams(location.search);
const urlValue = urlParams.has('refreshinterval')
? urlParams.get('refreshinterval')
: null;
if (urlValue) {
return options.loadFormat(urlValue);
}
// to handle refresh interval "off" state
return reduxInterval !== undefined ? reduxInterval : defaultValue;
});

// Reset to default when namespace changes
React.useEffect(() => {
if (prevNamespaceRef.current !== namespace) {
prevNamespaceRef.current = namespace;
setIntervalValue(defaultValue);
}
}, [namespace, defaultValue]);

// Persist to Redux whenever value changes
React.useEffect(() => {
dispatch(setIntervalAction(interval));
}, [interval, dispatch]);

useQueryParams({
key: 'refreshinterval',
value: interval,
setValue: setIntervalValue,
defaultValue: interval,
...options,
});

return [interval, setIntervalValue];
};
125 changes: 87 additions & 38 deletions src/components/pipelines-metrics/PipelinesAverageDuration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import {
ChartThemeColor,
ChartVoronoiContainer,
} from '@patternfly/react-charts/victory';
import { Card, CardBody, CardTitle } from '@patternfly/react-core';
import { Card, CardBody, CardTitle, Alert } from '@patternfly/react-core';
import { useFlag } from '@openshift-console/dynamic-plugin-sdk';
import { LoadingInline } from '../Loading';
import {
formatDate,
getDropDownDate,
Expand Down Expand Up @@ -77,6 +78,10 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
const { t } = useTranslation('plugin__pipelines-console-plugin');
const isDevConsoleProxyAvailable = useFlag(FLAGS.DEVCONSOLE_PROXY);
const [data, setData] = React.useState<SummaryResponse>();
const [loaded, setLoaded] = React.useState(false);
const [pipelineAverageDurationError, setPipelineAverageDurationError] =
React.useState<string | undefined>();
const abortControllerRef = React.useRef<AbortController>();
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);
Expand All @@ -90,11 +95,29 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
namespace = '-';
}

React.useEffect(() => {
return () => {
abortControllerRef.current?.abort();
};
}, []);

React.useEffect(() => {
setLoaded(false);
setPipelineAverageDurationError(undefined);
setData(undefined);
}, [namespace, timespan]);

const [tickValues, type] = getXaxisValues(timespan);

const date = getDropDownDate(timespan).toISOString();

const getSummaryData = (group_by: string) => {
abortControllerRef.current?.abort();
abortControllerRef.current = new AbortController();
setData(undefined);
setPipelineAverageDurationError(undefined);
setLoaded(false);

const summaryOpt = {
summary: 'avg_duration',
data_type: DataType.PipelineRun,
Expand All @@ -107,12 +130,23 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
summaryOpt,
undefined,
isDevConsoleProxyAvailable,
abortControllerRef.current.signal,
90000,
)
.then((d) => {
setLoaded(true);
setPipelineAverageDurationError(undefined);
setData(d);
})
.catch((e) => {
throw e;
if (e.name === 'AbortError') {
return;
}
setLoaded(true);
setPipelineAverageDurationError(
e.message || t('Failed to load average duration data'),
);
setData(undefined);
});
};

Expand Down Expand Up @@ -210,42 +244,57 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
<span>{t('Average duration')}</span>
</CardTitle>
<CardBody className="pipeline-overview__number-of-plr-card__body">
<div className="pipeline-overview__number-of-plr-card__bar-chart-div">
<Chart
containerComponent={
<ChartVoronoiContainer
labels={({ datum }) => `${datum.y}m`}
constrainToVisibleArea
/>
}
scale={{ x: 'time', y: 'linear' }}
domain={domainValue}
domainPadding={{ x: [30, 25] }}
height={145}
width={400}
padding={{
top: 10,
bottom: 55,
left: 50,
}}
themeColor={ChartThemeColor.blue}
>
<ChartAxis
tickValues={tickValues}
style={xAxisStyle}
tickFormat={xTickFormat}
label={showLabel ? dayLabel : ''}
/>
<ChartAxis
dependentAxis
style={yAxisStyle}
tickFormat={(v) => `${v}m`}
/>
<ChartGroup>
<ChartBar data={chartData} barWidth={18} />
</ChartGroup>
</Chart>
</div>
{pipelineAverageDurationError ? (
<Alert
variant="danger"
isInline
title={t('Unable to load average duration')}
className="pf-v5-u-my-lg pf-v5-u-ml-lg"
/>
) : (
<div className="pipeline-overview__number-of-plr-card__bar-chart-div">
{loaded ? (
<Chart
containerComponent={
<ChartVoronoiContainer
labels={({ datum }) => `${datum.y}m`}
constrainToVisibleArea
/>
}
scale={{ x: 'time', y: 'linear' }}
domain={domainValue}
domainPadding={{ x: [30, 25] }}
height={145}
width={400}
padding={{
top: 10,
bottom: 55,
left: 50,
}}
themeColor={ChartThemeColor.blue}
>
<ChartAxis
tickValues={tickValues}
style={xAxisStyle}
tickFormat={xTickFormat}
label={showLabel ? dayLabel : ''}
/>
<ChartAxis
dependentAxis
style={yAxisStyle}
tickFormat={(v) => `${v}m`}
/>
<ChartGroup>
<ChartBar data={chartData} barWidth={18} />
</ChartGroup>
</Chart>
) : (
<div className="pipeline-overview__number-of-plr-card__loading pf-v5-u-h-100">
<LoadingInline />
</div>
)}
</div>
)}
</CardBody>
</Card>
</>
Expand Down
Loading