Skip to content

Commit d57e3ee

Browse files
Merge pull request #836 from anwesha-palit-redhat/feat/SRVKP-SRVKP-9675
SRVKP-9675: Add Error Banner and Loading Spinner to Pipeline Metrics Page for Average Duration Card
2 parents 332b1f6 + ec1aa2f commit d57e3ee

3 files changed

Lines changed: 169 additions & 78 deletions

File tree

‎locales/en/plugin__pipelines-console-plugin.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
"Events triggering the webhook: ": "Events triggering the webhook: ",
195195
"Expand": "Expand",
196196
"Failed": "Failed",
197+
"Failed to load average duration data": "Failed to load average duration data",
197198
"Failed to load duration data": "Failed to load duration data",
198199
"Failed to load pipeline run status data": "Failed to load pipeline run status data",
199200
"Failed to load pipeline runs list": "Failed to load pipeline runs list",
@@ -584,6 +585,7 @@
584585
"Try it": "Try it",
585586
"Unable to detect Event listener URL": "Unable to detect Event listener URL",
586587
"Unable to find repository": "Unable to find repository",
588+
"Unable to load average duration": "Unable to load average duration",
587589
"Unable to load duration": "Unable to load duration",
588590
"Unable to load Pipeline": "Unable to load Pipeline",
589591
"Unable to load pipeline runs": "Unable to load pipeline runs",

‎src/components/pipelines-metrics/PipelinesAverageDuration.tsx‎

Lines changed: 87 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import {
1212
ChartThemeColor,
1313
ChartVoronoiContainer,
1414
} from '@patternfly/react-charts';
15-
import { Card, CardBody, CardTitle } from '@patternfly/react-core';
15+
import { Alert, Card, CardBody, CardTitle } from '@patternfly/react-core';
1616
import { useFlag } from '@openshift-console/dynamic-plugin-sdk';
17+
import { LoadingInline } from '../Loading';
1718
import {
1819
formatDate,
1920
getDropDownDate,
@@ -77,6 +78,10 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
7778
const { t } = useTranslation('plugin__pipelines-console-plugin');
7879
const isDevConsoleProxyAvailable = useFlag(FLAGS.DEVCONSOLE_PROXY);
7980
const [data, setData] = React.useState<SummaryResponse>();
81+
const [loaded, setLoaded] = React.useState(false);
82+
const [pipelineAverageDurationError, setPipelineAverageDurationError] =
83+
React.useState<string | undefined>();
84+
const abortControllerRef = React.useRef<AbortController>();
8085
const startTimespan = timespan - parsePrometheusDuration('1d');
8186
const endDate = new Date(Date.now()).setHours(0, 0, 0, 0);
8287
const startDate = new Date(Date.now() - startTimespan).setHours(0, 0, 0, 0);
@@ -90,11 +95,29 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
9095
namespace = '-';
9196
}
9297

98+
React.useEffect(() => {
99+
return () => {
100+
abortControllerRef.current?.abort();
101+
};
102+
}, []);
103+
104+
React.useEffect(() => {
105+
setLoaded(false);
106+
setPipelineAverageDurationError(undefined);
107+
setData(undefined);
108+
}, [namespace, timespan]);
109+
93110
const [tickValues, type] = getXaxisValues(timespan);
94111

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

97114
const getSummaryData = (group_by: string) => {
115+
abortControllerRef.current?.abort();
116+
abortControllerRef.current = new AbortController();
117+
setData(undefined);
118+
setPipelineAverageDurationError(undefined);
119+
setLoaded(false);
120+
98121
const summaryOpt = {
99122
summary: 'avg_duration',
100123
data_type: DataType.PipelineRun,
@@ -107,12 +130,23 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
107130
summaryOpt,
108131
undefined,
109132
isDevConsoleProxyAvailable,
133+
abortControllerRef.current.signal,
134+
90000,
110135
)
111136
.then((d) => {
137+
setLoaded(true);
138+
setPipelineAverageDurationError(undefined);
112139
setData(d);
113140
})
114141
.catch((e) => {
115-
throw e;
142+
if (e.name === 'AbortError') {
143+
return;
144+
}
145+
setLoaded(true);
146+
setPipelineAverageDurationError(
147+
e.message || t('Failed to load average duration data'),
148+
);
149+
setData(undefined);
116150
});
117151
};
118152

@@ -204,42 +238,57 @@ const PipelinesAverageDuration: React.FC<PipelinesAverageDurationProps> = ({
204238
<span>{t('Average duration')}</span>
205239
</CardTitle>
206240
<CardBody className="pipeline-overview__number-of-plr-card__body">
207-
<div className="pipeline-overview__number-of-plr-card__bar-chart-div">
208-
<Chart
209-
containerComponent={
210-
<ChartVoronoiContainer
211-
labels={({ datum }) => `${datum.y}m`}
212-
constrainToVisibleArea
213-
/>
214-
}
215-
scale={{ x: 'time', y: 'linear' }}
216-
domain={domainValue}
217-
domainPadding={{ x: [30, 25] }}
218-
height={145}
219-
width={400}
220-
padding={{
221-
top: 10,
222-
bottom: 55,
223-
left: 50,
224-
}}
225-
themeColor={ChartThemeColor.blue}
226-
>
227-
<ChartAxis
228-
tickValues={tickValues}
229-
style={xAxisStyle}
230-
tickFormat={xTickFormat}
231-
label={showLabel ? dayLabel : ''}
232-
/>
233-
<ChartAxis
234-
dependentAxis
235-
style={yAxisStyle}
236-
tickFormat={(v) => `${v}m`}
237-
/>
238-
<ChartGroup>
239-
<ChartBar data={chartData} barWidth={18} />
240-
</ChartGroup>
241-
</Chart>
242-
</div>
241+
{pipelineAverageDurationError ? (
242+
<Alert
243+
variant="danger"
244+
isInline
245+
title={t('Unable to load average duration')}
246+
className="pf-v5-u-my-lg pf-v5-u-ml-lg"
247+
/>
248+
) : (
249+
<div className="pipeline-overview__number-of-plr-card__bar-chart-div">
250+
{loaded ? (
251+
<Chart
252+
containerComponent={
253+
<ChartVoronoiContainer
254+
labels={({ datum }) => `${datum.y}m`}
255+
constrainToVisibleArea
256+
/>
257+
}
258+
scale={{ x: 'time', y: 'linear' }}
259+
domain={domainValue}
260+
domainPadding={{ x: [30, 25] }}
261+
height={145}
262+
width={400}
263+
padding={{
264+
top: 10,
265+
bottom: 55,
266+
left: 50,
267+
}}
268+
themeColor={ChartThemeColor.blue}
269+
>
270+
<ChartAxis
271+
tickValues={tickValues}
272+
style={xAxisStyle}
273+
tickFormat={xTickFormat}
274+
label={showLabel ? dayLabel : ''}
275+
/>
276+
<ChartAxis
277+
dependentAxis
278+
style={yAxisStyle}
279+
tickFormat={(v) => `${v}m`}
280+
/>
281+
<ChartGroup>
282+
<ChartBar data={chartData} barWidth={18} />
283+
</ChartGroup>
284+
</Chart>
285+
) : (
286+
<div className="pipeline-overview__number-of-plr-card__loading pf-v5-u-h-100">
287+
<LoadingInline />
288+
</div>
289+
)}
290+
</div>
291+
)}
243292
</CardBody>
244293
</Card>
245294
</>

‎src/components/pipelines-metrics/PipelinesAverageDurationK8s.tsx‎

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import _ from 'lodash';
3-
import * as classNames from 'classnames';
3+
import classNames from 'classnames';
44
import { useTranslation } from 'react-i18next';
55
import { DomainPropType, DomainTuple } from 'victory-core';
66
import {
@@ -12,7 +12,7 @@ import {
1212
ChartThemeColor,
1313
ChartVoronoiContainer,
1414
} from '@patternfly/react-charts';
15-
import { Card, CardBody, CardTitle } from '@patternfly/react-core';
15+
import { Alert, Card, CardBody, CardTitle } from '@patternfly/react-core';
1616
import {
1717
formatDate,
1818
getXaxisValues,
@@ -21,6 +21,7 @@ import {
2121
parsePrometheusDuration,
2222
} from '../pipelines-overview/dateTime';
2323
import { ALL_NAMESPACES_KEY } from '../../consts';
24+
import { LoadingInline } from '../Loading';
2425
import {
2526
usePipelineMetricsForAllNamespacePoll,
2627
usePipelineMetricsForNamespaceForPipelinePoll,
@@ -94,8 +95,13 @@ const PipelinesAverageDurationK8s: React.FC<PipelinesAverageDurationProps> = ({
9495
y: domainY || undefined,
9596
};
9697
const [tickValues, type] = getXaxisValues(timespan);
98+
const [averageDurationError, setAverageDurationError] = React.useState<
99+
string | null
100+
>(null);
101+
const [averageDurationLoading, setAverageDurationLoading] =
102+
React.useState(true);
97103

98-
const [totalPipelineRunsCountData] =
104+
const [totalPipelineRunsCountData, countError, countLoading] =
99105
parentName && namespace
100106
? usePipelineMetricsForNamespaceForPipelinePoll({
101107
namespace,
@@ -105,23 +111,26 @@ const PipelinesAverageDurationK8s: React.FC<PipelinesAverageDurationProps> = ({
105111
name: parentName,
106112
metricsQuery:
107113
PipelineQuery.PIPELINERUN_COUNT_FOR_NAMESPACE_FOR_PIPELINE,
114+
timeout: 90000,
108115
})
109116
: namespace == ALL_NAMESPACES_KEY
110117
? usePipelineMetricsForAllNamespacePoll({
111118
timespan,
112119
delay: interval,
113120
queryPrefix: MetricsQueryPrefix.TEKTON_PIPELINES_CONTROLLER,
114121
metricsQuery: PipelineQuery.PIPELINERUN_COUNT_FOR_ALL_NAMESPACE,
122+
timeout: 90000,
115123
})
116124
: usePipelineMetricsForNamespacePoll({
117125
namespace,
118126
timespan,
119127
delay: interval,
120128
queryPrefix: MetricsQueryPrefix.TEKTON_PIPELINES_CONTROLLER,
121129
metricsQuery: PipelineQuery.PIPELINERUN_COUNT_FOR_NAMESPACE,
130+
timeout: 90000,
122131
});
123132

124-
const [totalPipelineRunsDurationData] =
133+
const [totalPipelineRunsDurationData, durationError, durationLoading] =
125134
parentName && namespace
126135
? usePipelineMetricsForNamespaceForPipelinePoll({
127136
namespace,
@@ -131,22 +140,38 @@ const PipelinesAverageDurationK8s: React.FC<PipelinesAverageDurationProps> = ({
131140
name: parentName,
132141
metricsQuery:
133142
PipelineQuery.PIPELINERUN_DURATION_FOR_NAMESPACE_FOR_PIPELINE,
143+
timeout: 90000,
134144
})
135145
: namespace == ALL_NAMESPACES_KEY
136146
? usePipelineMetricsForAllNamespacePoll({
137147
timespan,
138148
delay: interval,
139149
queryPrefix: MetricsQueryPrefix.TEKTON_PIPELINES_CONTROLLER,
140150
metricsQuery: PipelineQuery.PIPELINERUN_DURATION_FOR_ALL_NAMESPACE,
151+
timeout: 90000,
141152
})
142153
: usePipelineMetricsForNamespacePoll({
143154
namespace,
144155
timespan,
145156
delay: interval,
146157
queryPrefix: MetricsQueryPrefix.TEKTON_PIPELINES_CONTROLLER,
147158
metricsQuery: PipelineQuery.PIPELINERUN_DURATION_FOR_NAMESPACE,
159+
timeout: 90000,
148160
});
149161

162+
React.useEffect(() => {
163+
const hasNonAbortError =
164+
(countError && countError.name !== 'AbortError') ||
165+
(durationError && durationError.name !== 'AbortError');
166+
setAverageDurationError(
167+
hasNonAbortError ? t('Unable to load average duration') : null,
168+
);
169+
}, [countError, durationError]);
170+
171+
React.useEffect(() => {
172+
setAverageDurationLoading(countLoading || durationLoading);
173+
}, [countLoading, durationLoading]);
174+
150175
const combinedData = totalPipelineRunsCountData?.data?.result[0]?.values?.map(
151176
(value1) => {
152177
const group_value = value1[0];
@@ -273,42 +298,57 @@ const PipelinesAverageDurationK8s: React.FC<PipelinesAverageDurationProps> = ({
273298
<span>{t('Average duration')}</span>
274299
</CardTitle>
275300
<CardBody className="pipeline-overview__number-of-plr-card__body">
276-
<div className="pipeline-overview__number-of-plr-card__bar-chart-div">
277-
<Chart
278-
containerComponent={
279-
<ChartVoronoiContainer
280-
labels={({ datum }) => `${datum.y}m`}
281-
constrainToVisibleArea
282-
/>
283-
}
284-
scale={{ x: 'time', y: 'linear' }}
285-
domain={domainValue}
286-
domainPadding={{ x: [30, 25] }}
287-
height={145}
288-
width={400}
289-
padding={{
290-
top: 10,
291-
bottom: 55,
292-
left: 50,
293-
}}
294-
themeColor={ChartThemeColor.blue}
295-
>
296-
<ChartAxis
297-
tickValues={tickValues}
298-
style={xAxisStyle}
299-
tickFormat={xTickFormat}
300-
label={showLabel ? dayLabel : ''}
301-
/>
302-
<ChartAxis
303-
dependentAxis
304-
style={yAxisStyle}
305-
tickFormat={(v) => `${v}m`}
306-
/>
307-
<ChartGroup>
308-
<ChartBar data={chartData} barWidth={18} />
309-
</ChartGroup>
310-
</Chart>
311-
</div>
301+
{averageDurationError ? (
302+
<Alert
303+
variant="danger"
304+
isInline
305+
title={t('Unable to load average duration')}
306+
className="pf-v5-u-my-lg pf-v5-u-ml-lg"
307+
/>
308+
) : (
309+
<div className="pipeline-overview__number-of-plr-card__bar-chart-div">
310+
{!averageDurationLoading ? (
311+
<Chart
312+
containerComponent={
313+
<ChartVoronoiContainer
314+
labels={({ datum }) => `${datum.y}m`}
315+
constrainToVisibleArea
316+
/>
317+
}
318+
scale={{ x: 'time', y: 'linear' }}
319+
domain={domainValue}
320+
domainPadding={{ x: [30, 25] }}
321+
height={145}
322+
width={400}
323+
padding={{
324+
top: 10,
325+
bottom: 55,
326+
left: 50,
327+
}}
328+
themeColor={ChartThemeColor.blue}
329+
>
330+
<ChartAxis
331+
tickValues={tickValues}
332+
style={xAxisStyle}
333+
tickFormat={xTickFormat}
334+
label={showLabel ? dayLabel : ''}
335+
/>
336+
<ChartAxis
337+
dependentAxis
338+
style={yAxisStyle}
339+
tickFormat={(v) => `${v}m`}
340+
/>
341+
<ChartGroup>
342+
<ChartBar data={chartData} barWidth={18} />
343+
</ChartGroup>
344+
</Chart>
345+
) : (
346+
<div className="pipeline-overview__number-of-plr-card__loading pf-v5-u-h-100">
347+
<LoadingInline />
348+
</div>
349+
)}
350+
</div>
351+
)}
312352
</CardBody>
313353
</Card>
314354
</>

0 commit comments

Comments
 (0)