Skip to content

Commit

Permalink
Merge pull request openshift-pipelines#18 from saumeya/summary-branch
Browse files Browse the repository at this point in the history
ODC-7441: Add API for Summary data and Results Data
  • Loading branch information
openshift-merge-bot[bot] authored Dec 8, 2023
2 parents 9f7258b + 3436d94 commit 8a5188d
Show file tree
Hide file tree
Showing 14 changed files with 368 additions and 49 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"react-i18next": "^11.7.3",
"react-router": "5.3.x",
"react-router-dom": "5.3.x",
"react-router-dom-v5-compat": "^6.11.2",
"resolve-url-loader": "2.x",
"sass": "^1.42.1",
"sass-loader": "^10.1.1",
Expand Down
14 changes: 13 additions & 1 deletion src/components/pipelines-metrics/PipelinesMetricsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
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';
Expand All @@ -17,6 +18,10 @@ const PipelinesMetricsPage: React.FC = () => {
parsePrometheusDuration('30s'),
);

const params = useParams();

const { ns: namespace, name: parentName } = params;

const sampleData = {
summary: {
total: 120,
Expand Down Expand Up @@ -74,6 +79,8 @@ const PipelinesMetricsPage: React.FC = () => {
grow={{ default: 'grow' }}
>
<PipelinesRunsNumbersChart
namespace={namespace}
parentName={parentName}
timespan={timespan}
domain={{ y: [0, 500] }}
/>
Expand All @@ -91,7 +98,12 @@ const PipelinesMetricsPage: React.FC = () => {
className="pipelines-metrics__cards"
grow={{ default: 'grow' }}
>
<PipelinesRunsDurationCard summaryData={sampleData.summary} />
<PipelinesRunsDurationCard
namespace={namespace}
parentName={parentName}
timespan={timespan}
interval={interval}
/>
</FlexItem>
</Flex>
</div>
Expand Down
37 changes: 29 additions & 8 deletions src/components/pipelines-overview/NamespaceDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,48 @@ import { useTranslation } from 'react-i18next';
import './PipelinesOverview.scss';
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
import { Project } from '../../types/openshift';
import { ALL_NAMESPACES_KEY } from '../../consts';

const NameSpaceDropdown = () => {
interface NameSpaceDropdownProps {
selected: string;
setSelected: (n: string) => void;
}

const NameSpaceDropdown: React.FC<NameSpaceDropdownProps> = ({
selected,
setSelected,
}) => {
const { t } = useTranslation('plugin__pipeline-console-plugin');
const [isOpen, setValue] = React.useState(false);
const toggleIsOpen = React.useCallback(() => setValue((v) => !v), []);
const setClosed = React.useCallback(() => setValue(false), []);
const [selected, setSelected] = React.useState('All');

const [projects, projectsLoaded] = useK8sWatchResource<Project[]>({
isList: true,
kind: 'Project',
optional: true,
});

const allNamespacesTitle = t('All');

const optionItems = React.useMemo(() => {
if (!projectsLoaded) {
return [];
}
const items = projects.map((item) => item.metadata.name);
const items = projects.map((item) => {
const { name } = item.metadata;
return { title: name, key: name };
});

items.sort((a, b) => alphanumericCompare(a, b));
if (
!items.some((option) => option.title === selected) &&
selected !== ALL_NAMESPACES_KEY
) {
items.push({ title: selected, key: selected }); // Add current namespace if it isn't included
}
items.sort((a, b) => alphanumericCompare(a.title, b.title));

items.unshift('All');
items.unshift({ title: allNamespacesTitle, key: ALL_NAMESPACES_KEY });
return items;
}, [projects, projectsLoaded]);

Expand All @@ -42,17 +61,19 @@ const NameSpaceDropdown = () => {
<DropdownItem
component="button"
key={key}
onClick={() => setSelected(name)}
onClick={() => setSelected(name.key)}
listItemClassName={'max-height-menu'}
className={'max-height-menu'}
>
{name}
{name.title}
</DropdownItem>
))}
isOpen={isOpen}
onSelect={setClosed}
toggle={
<DropdownToggle onToggle={toggleIsOpen}>{selected}</DropdownToggle>
<DropdownToggle onToggle={toggleIsOpen}>
{selected !== ALL_NAMESPACES_KEY ? selected : allNamespacesTitle}
</DropdownToggle>
}
isFullHeight={false}
className="pipeline-overview__variable-dropdown"
Expand Down
63 changes: 52 additions & 11 deletions src/components/pipelines-overview/PipelineRunsDurationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,52 @@ import {
GridItem,
} from '@patternfly/react-core';
import { SummaryProps } from './utils';
import { getResultsSummary } from '../utils/summary-api';
import { DataType } from '../utils/tekton-results';
import { ALL_NAMESPACES_KEY } from '../../consts';
import { formatTime, getDropDownDate } from './dateTime';

interface PipelinesRunsDurationProps {
summaryData: SummaryProps;
namespace: string;
timespan: number;
interval: number;
parentName?: string;
summaryData?: SummaryProps;
bordered?: boolean;
}

const PipelinesRunsDurationCard: React.FC<PipelinesRunsDurationProps> = ({
summaryData,
namespace,
timespan,
parentName,
interval,
bordered,
}) => {
const { t } = useTranslation('plugin__pipeline-console-plugin');
const [summaryData, setSummaryData] = React.useState<SummaryProps>({});
if (namespace == ALL_NAMESPACES_KEY) {
namespace = '-';
}

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}")`);

React.useEffect(() => {
getResultsSummary(namespace, {
summary: 'total_duration,avg_duration,max_duration',
data_type: DataType.PipelineRun,
filter,
})
.then((response) => {
setSummaryData(response.summary[0]);
})
.catch((e) => {
console.error('unable to post', e);
});
}, [namespace, timespan]);

return (
<>
Expand All @@ -40,41 +75,47 @@ const PipelinesRunsDurationCard: React.FC<PipelinesRunsDurationProps> = ({
<Divider />
<CardBody>
<Grid hasGutter className="pipeline-overview__duration-card__grid">
<GridItem span={8}>
<GridItem span={6}>
<span>
<MonitoringIcon className="pipeline-overview__duration-card__icon" />
{t('Average Duration')}
</span>
</GridItem>
<GridItem span={4}>
<GridItem span={6}>
<span className="pipeline-overview__duration-card__value">
{summaryData['avg-duration']}
{summaryData['avg_duration']
? formatTime(summaryData['avg_duration'])
: '-'}
</span>
</GridItem>
</Grid>
<Grid hasGutter className="pipeline-overview__duration-card__grid">
<GridItem span={8}>
<GridItem span={6}>
<span>
<InfoCircleIcon className="pipeline-overview__duration-card__info-icon" />
{t('Maximun')}
</span>
</GridItem>
<GridItem span={4}>
<GridItem span={6}>
<span className="pipeline-overview__duration-card__value">
{summaryData['max-duration']}
{summaryData['max_duration']
? formatTime(summaryData['max_duration'])
: '-'}
</span>
</GridItem>
</Grid>
<Grid hasGutter>
<GridItem span={8}>
<GridItem span={6}>
<span>
<HistoryIcon className="pipeline-overview__duration-card__icon" />
{t('Total Duration')}
</span>
</GridItem>
<GridItem span={4}>
<GridItem span={6}>
<span className="pipeline-overview__duration-card__value">
{summaryData['total-duration']}
{summaryData['total_duration']
? formatTime(summaryData['total_duration'])
: '-'}
</span>
</GridItem>
</Grid>
Expand Down
55 changes: 53 additions & 2 deletions src/components/pipelines-overview/PipelineRunsNumbersChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,30 @@ import {
import { Card, CardBody, CardTitle } from '@patternfly/react-core';
import {
formatDate,
getDropDownDate,
getXaxisValues,
parsePrometheusDuration,
} from './dateTime';
import { DataType } from '../utils/tekton-results';
import { SummaryResponse, getResultsSummary } from '../utils/summary-api';
import { ALL_NAMESPACES_KEY } from '../../consts';

interface PipelinesRunsNumbersChartProps {
namespace?: string;
timespan?: number;
interval?: number;
domain?: DomainPropType;
parentName?: string;
bordered?: boolean;
}
type DomainType = { x?: DomainTuple; y?: DomainTuple };

const PipelinesRunsNumbersChart: React.FC<PipelinesRunsNumbersChartProps> = ({
namespace,
timespan,
interval,
domain,
parentName,
bordered,
}) => {
const { t } = useTranslation('plugin__pipeline-console-plugin');
Expand All @@ -40,12 +50,53 @@ const PipelinesRunsNumbersChart: React.FC<PipelinesRunsNumbersChartProps> = ({
x: domainX || [startDate, endDate],
y: domainY || undefined,
};

const [data, setData] = React.useState<SummaryResponse>();

if (namespace == ALL_NAMESPACES_KEY) {
namespace = '-';
}
const tickValues = getXaxisValues(timespan);

const chartData = tickValues.map((value) => {
return { x: value, y: 100 };
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}")`);

React.useEffect(() => {
const summaryOpt = {
summary: 'total',
data_type: DataType.PipelineRun,
filter,
groupBy: 'day',
};

getResultsSummary(namespace, summaryOpt)
.then((d) => setData(d))
.catch((e) => {
throw e;
});
}, [namespace, date, interval]);

const chartData = tickValues?.map((value) => {
return {
x: value,
y: data?.summary.map((d) => {
return new Date(d.group_value * 1000).toDateString() ===
new Date(value).toDateString()
? d.total
: 0;
})[0],
};
});

const max = Math.max(...chartData.map((yVal) => yVal.y));
!isNaN(max) && max > 5
? (domainValue.y = [0, max])
: (domainValue.y = [0, 5]);

if (!domainY) {
let minY: number = _.minBy(chartData, 'y')?.y ?? 0;
let maxY: number = _.maxBy(chartData, 'y')?.y ?? 0;
Expand Down
12 changes: 6 additions & 6 deletions src/components/pipelines-overview/PipelineRunsStatusCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,19 @@ const PipelinesRunsStatusCard: React.FC<PipelinesRunsStatusCardProps> = ({
const donutData = [
{
x: t('Succeeded'),
y: Math.round((100 * summaryData.success) / summaryData.total),
y: Math.round((100 * summaryData.succeeded) / summaryData.total),
},
{
x: t('Failed'),
y: Math.round((100 * summaryData.failed) / summaryData.total),
},
{
x: t('Running'),
y: Math.round((100 * summaryData.running) / summaryData.total),
x: t('Unkwown'),
y: Math.round((100 * summaryData.unknown) / summaryData.total),
},
{
x: t('Pending'),
y: Math.round((100 * summaryData.pending) / summaryData.total),
x: t('Completed'),
y: Math.round((100 * summaryData.completed) / summaryData.total),
},
{
x: t('Cancelled'),
Expand Down Expand Up @@ -178,7 +178,7 @@ const PipelinesRunsStatusCard: React.FC<PipelinesRunsStatusCardProps> = ({
top: 20,
}}
subTitle={t('Succeeded')}
title={`${summaryData.success}/${summaryData.total}`}
title={`${summaryData.succeeded}/${summaryData.total}`}
width={350}
/>
</div>
Expand Down
Loading

0 comments on commit 8a5188d

Please sign in to comment.