Skip to content

Commit

Permalink
[AO] Reuse overview bucket size in AlertSummaryWidget (#150453)
Browse files Browse the repository at this point in the history
Resolves #150290

## Summary

- Reuse the overview bucket size in AlertSummaryWidget.
- Use `scaled date format` setting instead of `niceTimeFormatter` in
observability overview charts

Before

![image](https://user-images.githubusercontent.com/12370520/217490029-06fa183e-c2f2-4d86-a09d-bde77ac45e07.png)
After

![image](https://user-images.githubusercontent.com/12370520/217492571-207c27eb-e7a0-4d90-998f-7f79beca4c02.png)
  • Loading branch information
maryam-saeidi authored Feb 14, 2023
1 parent 3ae7d70 commit 6e58c2d
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jest.mock('react-router-dom', () => ({
}));

describe('APMSection', () => {
const bucketSize = { intervalString: '60s', bucketSize: 60, dateFormat: 'YYYY-MM-DD HH:mm' };

beforeAll(() => {
jest.spyOn(hasDataHook, 'useHasData').mockReturnValue({
hasDataMap: {
Expand Down Expand Up @@ -81,7 +83,7 @@ describe('APMSection', () => {
refetch: jest.fn(),
});
const { getByRole, getByText, queryAllByTestId } = render(
<APMSection bucketSize={{ intervalString: '60s', bucketSize: 60 }} />
<APMSection bucketSize={bucketSize} />
);

expect(getByRole('heading')).toHaveTextContent('Services');
Expand All @@ -98,7 +100,7 @@ describe('APMSection', () => {
refetch: jest.fn(),
});
const { getByRole, getByText, queryAllByTestId } = render(
<APMSection bucketSize={{ intervalString: '60s', bucketSize: 60 }} />
<APMSection bucketSize={bucketSize} />
);

expect(getByRole('heading')).toHaveTextContent('Services');
Expand All @@ -114,7 +116,7 @@ describe('APMSection', () => {
refetch: jest.fn(),
});
const { getByRole, queryAllByText, getByTestId } = render(
<APMSection bucketSize={{ intervalString: '60s', bucketSize: 60 }} />
<APMSection bucketSize={bucketSize} />
);

expect(getByRole('heading')).toHaveTextContent('Services');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Settings,
XYBrushEvent,
} from '@elastic/charts';
import { timeFormatter } from '@elastic/charts/dist/utils/data/formatters';
import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiIcon } from '@elastic/eui';
import numeral from '@elastic/numeral';
import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -85,7 +86,9 @@ export function APMSection({ bucketSize }: Props) {
const min = moment.utc(absoluteStart).valueOf();
const max = moment.utc(absoluteEnd).valueOf();

const formatter = niceTimeFormatter([min, max]);
const formatter = bucketSize?.dateFormat
? timeFormatter(bucketSize?.dateFormat)
: niceTimeFormatter([min, max]);

const isLoading = status === FETCH_STATUS.LOADING;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Settings,
XYBrushEvent,
} from '@elastic/charts';
import { timeFormatter } from '@elastic/charts/dist/utils/data/formatters';
import { EuiFlexGroup, EuiFlexItem, euiPaletteColorBlind, EuiSpacer, EuiTitle } from '@elastic/eui';
import numeral from '@elastic/numeral';
import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -83,7 +84,9 @@ export function LogsSection({ bucketSize }: Props) {
const min = moment.utc(absoluteStart).valueOf();
const max = moment.utc(absoluteEnd).valueOf();

const formatter = niceTimeFormatter([min, max]);
const formatter = bucketSize?.dateFormat
? timeFormatter(bucketSize?.dateFormat)
: niceTimeFormatter([min, max]);

const { appLink, stats, series } = data || {};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
TickFormatter,
XYBrushEvent,
} from '@elastic/charts';
import { timeFormatter } from '@elastic/charts/dist/utils/data/formatters';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import numeral from '@elastic/numeral';
import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -81,7 +82,9 @@ export function UptimeSection({ bucketSize }: Props) {
const min = moment.utc(absoluteStart).valueOf();
const max = moment.utc(absoluteEnd).valueOf();

const formatter = niceTimeFormatter([min, max]);
const formatter = bucketSize?.dateFormat
? timeFormatter(bucketSize?.dateFormat)
: niceTimeFormatter([min, max]);

const isLoading = status === FETCH_STATUS.LOADING;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jest.mock('react-router-dom', () => ({
}));

describe('UXSection', () => {
const bucketSize = { intervalString: '60s', bucketSize: 60, dateFormat: 'YYYY-MM-DD HH:mm' };

beforeAll(() => {
jest.spyOn(hasDataHook, 'useHasData').mockReturnValue({
hasDataMap: {
Expand All @@ -44,9 +46,7 @@ describe('UXSection', () => {
status: fetcherHook.FETCH_STATUS.SUCCESS,
refetch: jest.fn(),
});
const { getByText, getAllByText } = render(
<UXSection bucketSize={{ bucketSize: 60, intervalString: '60s' }} />
);
const { getByText, getAllByText } = render(<UXSection bucketSize={bucketSize} />);

expect(getByText('User Experience')).toBeInTheDocument();
expect(getByText('Show dashboard')).toBeInTheDocument();
Expand Down Expand Up @@ -79,7 +79,7 @@ describe('UXSection', () => {
refetch: jest.fn(),
});
const { getByText, queryAllByText, getAllByText } = render(
<UXSection bucketSize={{ bucketSize: 60, intervalString: '60s' }} />
<UXSection bucketSize={bucketSize} />
);

expect(getByText('User Experience')).toBeInTheDocument();
Expand All @@ -94,7 +94,7 @@ describe('UXSection', () => {
refetch: jest.fn(),
});
const { getByText, queryAllByText, getAllByText } = render(
<UXSection bucketSize={{ bucketSize: 60, intervalString: '60s' }} />
<UXSection bucketSize={bucketSize} />
);

expect(getByText('User Experience')).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import { i18n } from '@kbn/i18n';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { loadRuleAggregations } from '@kbn/triggers-actions-ui-plugin/public';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { calculateTimeRangeBucketSize } from '../../../overview/containers/overview_page/helpers/calculate_bucket_size';
import {
DEFAULT_DATE_FORMAT,
DEFAULT_INTERVAL,
} from '../../../overview/containers/overview_page/constants';
import { useToasts } from '../../../../hooks/use_toast';
import {
alertSearchBarStateContainer,
Expand Down Expand Up @@ -77,9 +82,9 @@ function InternalAlertsPage() {
const { hasAnyData, isAllRequestsComplete } = useHasData();
const [esQuery, setEsQuery] = useState<{ bool: BoolQuery }>();
const timeBuckets = useTimeBuckets();
const alertSummaryTimeRange = useMemo(
const bucketSize = useMemo(
() =>
getAlertSummaryTimeRange(
calculateTimeRangeBucketSize(
{
from: alertSearchBarStateProps.rangeFrom,
to: alertSearchBarStateProps.rangeTo,
Expand All @@ -88,6 +93,18 @@ function InternalAlertsPage() {
),
[alertSearchBarStateProps.rangeFrom, alertSearchBarStateProps.rangeTo, timeBuckets]
);
const alertSummaryTimeRange = useMemo(
() =>
getAlertSummaryTimeRange(
{
from: alertSearchBarStateProps.rangeFrom,
to: alertSearchBarStateProps.rangeTo,
},
bucketSize?.intervalString || DEFAULT_INTERVAL,
bucketSize?.dateFormat || DEFAULT_DATE_FORMAT
),
[alertSearchBarStateProps.rangeFrom, alertSearchBarStateProps.rangeTo, bucketSize]
);

useBreadcrumbs([
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

export const CAPABILITIES_KEYS = ['logs', 'infrastructure', 'apm', 'uptime'];

export const DEFAULT_INTERVAL = '60s';
export const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm';

export const ALERTS_TABLE_ID = 'xpack.observability.overview.alert.table';
export const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.overview.alert.tableState';
export const ALERTS_PER_PAGE = 10;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { TimeBuckets } from '@kbn/data-plugin/common';
import { calculateTimeRangeBucketSize } from './calculate_bucket_size';

describe('calculateTimeRangeBucketSize', () => {
const timeBucketConfig = {
'histogram:maxBars': 4,
'histogram:barTarget': 3,
dateFormat: 'YYYY-MM-DD',
'dateFormat:scaled': [
['', 'HH:mm:ss.SSS'],
['PT1S', 'HH:mm:ss'],
['PT1M', 'HH:mm'],
['PT1H', 'YYYY-MM-DD HH:mm'],
['P1DT', 'YYYY-MM-DD'],
['P1YT', 'YYYY'],
],
};
const timeBuckets = new TimeBuckets(timeBucketConfig);

it.each([
// 15 minutes
['2023-01-09T12:07:54.441Z', '2023-01-09T12:22:54.441Z', 60, '60s', 'HH:mm'],
['now-15m', 'now', 60, '60s', 'HH:mm'],
// 30 minutes
['2023-01-09T11:53:43.605Z', '2023-01-09T12:23:43.605Z', 60, '60s', 'HH:mm'],
// 1 hour
['2023-01-09T11:22:05.728Z', '2023-01-09T12:22:05.728Z', 60, '60s', 'HH:mm'],
// 24 hours
['2023-01-08T12:00:00.000Z', '2023-01-09T12:24:30.853Z', 600, '600s', 'HH:mm'],
// 7 days
['2023-01-01T23:00:00.000Z', '2023-01-09T12:29:38.101Z', 3600, '3600s', 'YYYY-MM-DD HH:mm'],
// 30 days
['2022-12-09T23:00:00.000Z', '2023-01-09T12:30:13.717Z', 43200, '43200s', 'YYYY-MM-DD HH:mm'],
// 90 days
['2022-10-10T22:00:00.000Z', '2023-01-09T12:32:11.537Z', 43200, '43200s', 'YYYY-MM-DD HH:mm'],
// 1 year
['2022-01-08T23:00:00.000Z', '2023-01-09T12:33:09.906Z', 86400, '86400s', 'YYYY-MM-DD'],
])(
`Input: [%s, %s], Output: bucketSize: %s, intervalString: %s, dateFormat: %s `,
(from, to, bucketSize, intervalString, dateFormat) => {
expect(calculateTimeRangeBucketSize({ from, to }, timeBuckets)).toEqual({
bucketSize,
intervalString,
dateFormat,
});
}
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,36 @@
* 2.0.
*/

import { TimeBuckets } from '@kbn/data-plugin/common';
import { TimeRange } from '@kbn/es-query';
import { getAbsoluteTime } from '../../../../../utils/date';
import { DEFAULT_INTERVAL } from '../constants';
import { Bucket, BucketSize } from '../types';
import { getBucketSize } from '../../../../../utils/get_bucket_size';

export function calculateBucketSize({ start, end }: Bucket): BucketSize {
export function calculateBucketSize({ start, end, timeBuckets }: Bucket): BucketSize {
if (start && end) {
return getBucketSize({ start, end, minInterval: '60s' });
const { bucketSize, intervalString } = getBucketSize({
start,
end,
minInterval: DEFAULT_INTERVAL,
});
timeBuckets.setInterval(intervalString);

return {
bucketSize,
intervalString,
dateFormat: timeBuckets.getScaledDateFormat(),
};
}
}

export function calculateTimeRangeBucketSize(
{ from, to }: TimeRange,
timeBuckets: TimeBuckets
): BucketSize {
const start = getAbsoluteTime(from);
const end = getAbsoluteTime(to, { roundUp: true });

return calculateBucketSize({ start, end, timeBuckets });
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ import { getNewsFeed } from '../../../../services/get_news_feed';
import { buildEsQuery } from '../../../../utils/build_es_query';
import { getAlertSummaryTimeRange } from '../../../../utils/alert_summary_widget';

import { ALERTS_PER_PAGE, ALERTS_TABLE_ID } from './constants';
import {
ALERTS_PER_PAGE,
ALERTS_TABLE_ID,
DEFAULT_DATE_FORMAT,
DEFAULT_INTERVAL,
} from './constants';
import { calculateBucketSize, useOverviewMetrics } from './helpers';

export function OverviewPage() {
Expand Down Expand Up @@ -91,33 +96,35 @@ export function OverviewPage() {
to: relativeEnd,
})
);

const timeBuckets = useTimeBuckets();
const bucketSize = useMemo(
() =>
calculateBucketSize({
start: absoluteStart,
end: absoluteEnd,
timeBuckets,
}),
[absoluteStart, absoluteEnd, timeBuckets]
);
const alertSummaryTimeRange = useMemo(
() =>
getAlertSummaryTimeRange(
{
from: relativeStart,
to: relativeEnd,
},
timeBuckets
bucketSize?.intervalString || DEFAULT_INTERVAL,
bucketSize?.dateFormat || DEFAULT_DATE_FORMAT
),
[relativeEnd, relativeStart, timeBuckets]
[bucketSize, relativeEnd, relativeStart]
);

const chartThemes = {
theme: charts.theme.useChartsTheme(),
baseTheme: charts.theme.useChartsBaseTheme(),
};

const bucketSize = useMemo(
() =>
calculateBucketSize({
start: absoluteStart,
end: absoluteEnd,
}),
[absoluteStart, absoluteEnd]
);

useEffect(() => {
setEsQuery(
buildEsQuery({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
* 2.0.
*/

import { TimeBuckets } from '@kbn/data-plugin/common';

export interface Bucket {
start?: number;
end?: number;
timeBuckets: TimeBuckets;
}
export type BucketSize = { bucketSize: number; intervalString: string } | undefined;
export type BucketSize =
| { bucketSize: number; intervalString: string; dateFormat: string }
| undefined;
Loading

0 comments on commit 6e58c2d

Please sign in to comment.