Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan
- [#110](https://github.com/kobsio/kobs/pull/110): Fix Dashboard tabs showing wrong variables.
- [#111](https://github.com/kobsio/kobs/pull/111): Fix usage of `memo` in Dashboards and fix resources table for CRDs when a value is undefined.
- [#114](https://github.com/kobsio/kobs/pull/114): Fix span order and additional fields for Jaeger plugin.
- [#118](https://github.com/kobsio/kobs/pull/118): Fix `null is not an object (evaluating 'e[Symbol.iterator]')` error for Prometheus charts.

### Changed

Expand Down
20 changes: 19 additions & 1 deletion plugins/applications/src/components/panel/details/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
ListItem,
ListVariant,
} from '@patternfly/react-core';
import { TopologyIcon, UsersIcon } from '@patternfly/react-icons';
import { Link } from 'react-router-dom';
import React from 'react';
import { UsersIcon } from '@patternfly/react-icons';

import { ExternalLink, Title } from '@kobsio/plugin-core';
import { DashboardsWrapper } from '@kobsio/plugin-dashboards';
Expand Down Expand Up @@ -42,6 +42,7 @@ const Details: React.FunctionComponent<IDetailsProps> = ({ application, close }:
<div>
<p>{application.description}</p>
{(application.teams && application.teams.length > 0) ||
(application.dependencies && application.dependencies.length > 0) ||
(application.links && application.links.length > 0) ? (
<List variant={ListVariant.inline}>
{application.teams && application.teams.length > 0
Expand All @@ -61,6 +62,23 @@ const Details: React.FunctionComponent<IDetailsProps> = ({ application, close }:
))
: null}

{application.dependencies && application.dependencies.length > 0
? application.dependencies.map((dependency, index) => (
<ListItem key={index}>
<Link
key={index}
to={`/applications/${dependency.cluster ? dependency.cluster : application.cluster}/${
dependency.namespace ? dependency.namespace : application.namespace
}/${dependency.name}`}
>
<Button variant={ButtonVariant.link} isInline={true} icon={<TopologyIcon />}>
{dependency.name}
</Button>
</Link>
</ListItem>
))
: null}

{application.links && application.links.length
? application.links.map((link, index) => (
<ListItem key={index}>
Expand Down
26 changes: 11 additions & 15 deletions plugins/prometheus/src/components/page/PageChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,30 @@ import { ResponsiveLineCanvas, Serie } from '@nivo/line';
import { SquareIcon } from '@patternfly/react-icons';
import { TooltipWrapper } from '@nivo/tooltip';

import { convertMetrics, formatAxisBottom } from '../../utils/helpers';
import { COLOR_SCALE } from '../../utils/colors';
import { IMetrics } from '../../utils/interfaces';
import { ISeries } from '../../utils/interfaces';
import PageChartLegend from './PageChartLegend';
import { formatAxisBottom } from '../../utils/helpers';

interface IPageChartProps {
queries: string[];
metrics: IMetrics;
series: ISeries;
}

// The PageChart component is used to render the chart for the given metrics. Above the chart we display two toggle
// groups so that the user can adjust the basic style of the chart. The user can switch between a line and area chart
// and between a stacked and unstacked visualization. At the bottom of the page we are including the PageChartLegend
// component to render the legend for the metrics.
const PageChart: React.FunctionComponent<IPageChartProps> = ({ queries, metrics }: IPageChartProps) => {
// series is an array for the converted metrics, which can be used by nivo. We convert the metrics to a series, so
// that we have to do this onyl once and not everytime the selected metrics are changed.
const seriesData = convertMetrics(metrics.metrics, metrics.startTime, metrics.endTime, metrics.min, metrics.max);

const PageChart: React.FunctionComponent<IPageChartProps> = ({ queries, series }: IPageChartProps) => {
const [type, setType] = useState<string>('line');
const [stacked, setStacked] = useState<boolean>(false);
const [selectedSeries, setSelectedSeries] = useState<Serie[]>(seriesData.series);
const [selectedSeries, setSelectedSeries] = useState<Serie[]>(series.series);

// select is used to select a single metric, which should be shown in the rendered chart. If the currently selected
// metric is clicked again, the filter will be removed and all metrics will be shown in the chart.
const select = (color: string, metric: Serie): void => {
if (selectedSeries.length === 1 && selectedSeries[0].label === metric.label) {
setSelectedSeries(seriesData.series);
setSelectedSeries(series.series);
} else {
setSelectedSeries([{ ...metric, color: color }]);
}
Expand All @@ -58,7 +54,7 @@ const PageChart: React.FunctionComponent<IPageChartProps> = ({ queries, metrics
<div style={{ height: '350px' }}>
<ResponsiveLineCanvas
axisBottom={{
format: formatAxisBottom(metrics.startTime, metrics.endTime),
format: formatAxisBottom(series.startTime, series.endTime),
}}
axisLeft={{
format: '>-.2f',
Expand All @@ -84,7 +80,7 @@ const PageChart: React.FunctionComponent<IPageChartProps> = ({ queries, metrics
}}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
tooltip={(tooltip) => {
const isFirstHalf = new Date(tooltip.point.data.x).getTime() < (metrics.endTime + metrics.startTime) / 2;
const isFirstHalf = new Date(tooltip.point.data.x).getTime() < (series.endTime + series.startTime) / 2;

return (
<TooltipWrapper anchor={isFirstHalf ? 'right' : 'left'} position={[0, 20]}>
Expand All @@ -101,20 +97,20 @@ const PageChart: React.FunctionComponent<IPageChartProps> = ({ queries, metrics
<b>{tooltip.point.data.xFormatted}</b>
</div>
<div>
<SquareIcon color={tooltip.point.color} /> {seriesData.labels[tooltip.point.id.split('.')[0]]}:{' '}
<SquareIcon color={tooltip.point.color} /> {series.labels[tooltip.point.id.split('.')[0]]}:{' '}
{tooltip.point.data.yFormatted}
</div>
</div>
</TooltipWrapper>
);
}}
xScale={{ max: new Date(metrics.endTime), min: new Date(metrics.startTime), type: 'time' }}
xScale={{ max: new Date(series.endTime), min: new Date(series.startTime), type: 'time' }}
yScale={{ stacked: stacked, type: 'linear' }}
yFormat=" >-.4f"
/>
</div>
<p>&nbsp;</p>
<PageChartLegend queries={queries} series={seriesData.series} selected={selectedSeries} select={select} />
<PageChartLegend queries={queries} series={series.series} selected={selectedSeries} select={select} />
</CardBody>
</Card>
);
Expand Down
15 changes: 10 additions & 5 deletions plugins/prometheus/src/components/page/PageChartWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { QueryObserverResult, useQuery } from 'react-query';
import React from 'react';
import { useHistory } from 'react-router-dom';

import { IMetrics, IOptions } from '../../utils/interfaces';
import { IOptions, ISeries } from '../../utils/interfaces';
import PageChart from './PageChart';
import { convertMetrics } from '../../utils/helpers';

interface IPageChartWrapperProps extends IOptions {
name: string;
Expand All @@ -21,7 +22,7 @@ const PageChartWrapper: React.FunctionComponent<IPageChartWrapperProps> = ({
}: IPageChartWrapperProps) => {
const history = useHistory();

const { isError, isLoading, error, data, refetch } = useQuery<IMetrics, Error>(
const { isError, isLoading, error, data, refetch } = useQuery<ISeries, Error>(
['prometheus/metrics', name, queries, resolution, times],
async () => {
try {
Expand All @@ -39,7 +40,11 @@ const PageChartWrapper: React.FunctionComponent<IPageChartWrapperProps> = ({
const json = await response.json();

if (response.status >= 200 && response.status < 300) {
return json;
if (json && json.metrics) {
return convertMetrics(json.metrics, json.startTime, json.endTime, json.min, json.max);
} else {
return { endTime: times.timeEnd, labels: {}, max: 0, min: 0, series: [], startTime: times.timeStart };
}
} else {
if (json.error) {
throw new Error(json.error);
Expand Down Expand Up @@ -69,7 +74,7 @@ const PageChartWrapper: React.FunctionComponent<IPageChartWrapperProps> = ({
actionLinks={
<React.Fragment>
<AlertActionLink onClick={(): void => history.push('/')}>Home</AlertActionLink>
<AlertActionLink onClick={(): Promise<QueryObserverResult<IMetrics, Error>> => refetch()}>
<AlertActionLink onClick={(): Promise<QueryObserverResult<ISeries, Error>> => refetch()}>
Retry
</AlertActionLink>
</React.Fragment>
Expand All @@ -84,7 +89,7 @@ const PageChartWrapper: React.FunctionComponent<IPageChartWrapperProps> = ({
return null;
}

return <PageChart queries={queries} metrics={data} />;
return <PageChart queries={queries} series={data} />;
};

export default PageChartWrapper;
6 changes: 5 additions & 1 deletion plugins/prometheus/src/components/panel/ChartWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ export const ChartWrapper: React.FunctionComponent<IChartWrapperProps> = ({
const json = await response.json();

if (response.status >= 200 && response.status < 300) {
return convertMetrics(json.metrics, json.startTime, json.endTime, json.min, json.max);
if (json && json.metrics) {
return convertMetrics(json.metrics, json.startTime, json.endTime, json.min, json.max);
} else {
return { endTime: times.timeEnd, labels: {}, max: 0, min: 0, series: [], startTime: times.timeStart };
}
} else {
if (json.error) {
throw new Error(json.error);
Expand Down