Skip to content

Commit b72348f

Browse files
committed
move validation to server side
1 parent 0531c67 commit b72348f

9 files changed

Lines changed: 114 additions & 69 deletions

File tree

src/plugins/vis_type_timeseries/public/application/lib/validate_interval.ts renamed to src/plugins/vis_type_timeseries/common/validate_interval.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,29 @@
77
*/
88

99
import { i18n } from '@kbn/i18n';
10-
import { GTE_INTERVAL_RE } from '../../../common/interval_regexp';
11-
import { search } from '../../../../../plugins/data/public';
10+
import { GTE_INTERVAL_RE } from './interval_regexp';
11+
import { parseInterval, TimeRangeBounds } from '../../data/common';
1212

13-
import type { TimeRangeBounds } from '../../../../data/common';
14-
import type { TimeseriesVisParams } from '../../types';
13+
export class ValidateIntervalError extends Error {
14+
constructor() {
15+
super(
16+
i18n.translate('visTypeTimeseries.validateInterval.notifier.maxBucketsExceededErrorMessage', {
17+
defaultMessage:
18+
'Your query attempted to fetch too much data. Reducing the time range or changing the interval used usually fixes the issue.',
19+
})
20+
);
21+
}
22+
23+
public get name() {
24+
return this.constructor.name;
25+
}
1526

16-
const { parseInterval } = search.aggs;
27+
public get errBody() {
28+
return this.message;
29+
}
30+
}
1731

18-
export function validateInterval(
19-
bounds: TimeRangeBounds,
20-
panel: TimeseriesVisParams,
21-
maxBuckets: number
22-
) {
23-
const { interval } = panel;
32+
export function validateInterval(bounds: TimeRangeBounds, interval: string, maxBuckets: number) {
2433
const { min, max } = bounds;
2534
// No need to check auto it will return around 100
2635
if (!interval) return;
@@ -33,15 +42,7 @@ export function validateInterval(
3342
const span = max!.valueOf() - min!.valueOf();
3443
const buckets = Math.floor(span / duration.asMilliseconds());
3544
if (buckets > maxBuckets) {
36-
throw new Error(
37-
i18n.translate(
38-
'visTypeTimeseries.validateInterval.notifier.maxBucketsExceededErrorMessage',
39-
{
40-
defaultMessage:
41-
'Your query attempted to fetch too much data. Reducing the time range or changing the interval used usually fixes the issue.',
42-
}
43-
)
44-
);
45+
throw new ValidateIntervalError();
4546
}
4647
}
4748
}

src/plugins/vis_type_timeseries/public/request_handler.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
* Side Public License, v 1.
77
*/
88

9-
import { KibanaContext } from '../../data/public';
10-
119
import { getTimezone } from './application/lib/get_timezone';
12-
import { validateInterval } from './application/lib/validate_interval';
1310
import { getUISettings, getDataStart, getCoreStart } from './services';
14-
import { MAX_BUCKETS_SETTING, ROUTES } from '../common/constants';
15-
import { TimeseriesVisParams } from './types';
11+
import { ROUTES } from '../common/constants';
12+
13+
import type { TimeseriesVisParams } from './types';
1614
import type { TimeseriesVisData } from '../common/types';
15+
import type { KibanaContext } from '../../data/public';
1716

1817
interface MetricsRequestHandlerParams {
1918
input: KibanaContext | null;
@@ -37,10 +36,6 @@ export const metricsRequestHandler = async ({
3736
const parsedTimeRange = data.query.timefilter.timefilter.calculateBounds(input?.timeRange!);
3837

3938
if (visParams && visParams.id && !visParams.isModelInvalid) {
40-
const maxBuckets = config.get<number>(MAX_BUCKETS_SETTING);
41-
42-
validateInterval(parsedTimeRange, visParams, maxBuckets);
43-
4439
const untrackSearch =
4540
dataSearch.session.isCurrentSession(searchSessionId) &&
4641
dataSearch.session.trackSearch({

src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import _ from 'lodash';
1010

1111
import { Framework } from '../plugin';
12-
import type { TimeseriesVisData } from '../../common/types';
12+
import type { TimeseriesVisData, FetchedIndexPattern, Series } from '../../common/types';
1313
import { PANEL_TYPES } from '../../common/enums';
1414
import type {
1515
VisTypeTimeseriesVisDataRequest,
@@ -20,6 +20,8 @@ import { getSeriesData } from './vis_data/get_series_data';
2020
import { getTableData } from './vis_data/get_table_data';
2121
import { getEsQueryConfig } from './vis_data/helpers/get_es_query_uisettings';
2222
import { getCachedIndexPatternFetcher } from './search_strategies/lib/cached_index_pattern_fetcher';
23+
import { MAX_BUCKETS_SETTING } from '../../common/constants';
24+
import { getIntervalAndTimefield } from './vis_data/get_interval_and_timefield';
2325

2426
export async function getVisData(
2527
requestContext: VisTypeTimeseriesRequestHandlerContext,
@@ -32,15 +34,41 @@ export async function getVisData(
3234
const esQueryConfig = await getEsQueryConfig(uiSettings);
3335

3436
const promises = request.body.panels.map((panel) => {
37+
const cachedIndexPatternFetcher = getCachedIndexPatternFetcher(indexPatternsService, {
38+
fetchKibanaIndexForStringIndexes: Boolean(panel.use_kibana_indexes),
39+
});
3540
const services: VisTypeTimeseriesRequestServices = {
3641
esQueryConfig,
3742
esShardTimeout,
3843
indexPatternsService,
3944
uiSettings,
45+
cachedIndexPatternFetcher,
4046
searchStrategyRegistry: framework.searchStrategyRegistry,
41-
cachedIndexPatternFetcher: getCachedIndexPatternFetcher(indexPatternsService, {
42-
fetchKibanaIndexForStringIndexes: Boolean(panel.use_kibana_indexes),
43-
}),
47+
buildSeriesMetaParams: async (
48+
index: FetchedIndexPattern,
49+
useKibanaIndexes: boolean,
50+
series?: Series
51+
) => {
52+
/** This part of code is required to try to get the default timefield for string indices.
53+
* The rest of the functionality available for Kibana indexes should not be active **/
54+
if (!useKibanaIndexes && index.indexPatternString) {
55+
index = await cachedIndexPatternFetcher(index.indexPatternString, true);
56+
}
57+
58+
const maxBuckets = await uiSettings.get<number>(MAX_BUCKETS_SETTING);
59+
const { min, max } = request.body.timerange;
60+
61+
return getIntervalAndTimefield(
62+
panel,
63+
index,
64+
{
65+
min,
66+
max,
67+
maxBuckets,
68+
},
69+
series
70+
);
71+
},
4472
};
4573

4674
return panel.type === PANEL_TYPES.TABLE

src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@ import { FetchedIndexPattern, Panel, Series } from '../../../common/types';
1111

1212
describe('getIntervalAndTimefield(panel, series)', () => {
1313
const index: FetchedIndexPattern = {} as FetchedIndexPattern;
14+
const params = {
15+
min: '2017-01-01T00:00:00Z',
16+
max: '2017-01-01T01:00:00Z',
17+
maxBuckets: 1000,
18+
};
1419

1520
test('returns the panel interval and timefield', () => {
1621
const panel = { time_field: '@timestamp', interval: 'auto' } as Panel;
1722
const series = {} as Series;
1823

19-
expect(getIntervalAndTimefield(panel, index, series)).toEqual({
24+
expect(getIntervalAndTimefield(panel, index, params, series)).toEqual({
2025
timeField: '@timestamp',
2126
interval: 'auto',
2227
});
@@ -30,7 +35,7 @@ describe('getIntervalAndTimefield(panel, series)', () => {
3035
series_time_field: 'time',
3136
} as unknown) as Series;
3237

33-
expect(getIntervalAndTimefield(panel, index, series)).toEqual({
38+
expect(getIntervalAndTimefield(panel, index, params, series)).toEqual({
3439
timeField: 'time',
3540
interval: '1m',
3641
});

src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@
55
* in compliance with, at your election, the Elastic License 2.0 or the Server
66
* Side Public License, v 1.
77
*/
8-
8+
import moment from 'moment';
99
import { AUTO_INTERVAL } from '../../../common/constants';
1010
import { validateField } from '../../../common/fields_utils';
11+
import { validateInterval } from '../../../common/validate_interval';
1112

1213
import type { FetchedIndexPattern, Panel, Series } from '../../../common/types';
1314

14-
export function getIntervalAndTimefield(panel: Panel, index: FetchedIndexPattern, series?: Series) {
15+
interface IntervalParams {
16+
min: string;
17+
max: string;
18+
maxBuckets: number;
19+
}
20+
21+
export function getIntervalAndTimefield(
22+
panel: Panel,
23+
index: FetchedIndexPattern,
24+
{ min, max, maxBuckets }: IntervalParams,
25+
series?: Series
26+
) {
1527
const timeField =
1628
(series?.override_index_pattern ? series.series_time_field : panel.time_field) ||
1729
index.indexPattern?.timeFieldName;
@@ -28,6 +40,15 @@ export function getIntervalAndTimefield(panel: Panel, index: FetchedIndexPattern
2840
maxBars = series.series_max_bars;
2941
}
3042

43+
validateInterval(
44+
{
45+
min: moment.utc(min),
46+
max: moment.utc(max),
47+
},
48+
interval,
49+
maxBuckets
50+
);
51+
3152
return {
3253
maxBars,
3354
timeField,

src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ import { processBucket } from './table/process_bucket';
1818

1919
import { createFieldsFetcher } from '../search_strategies/lib/fields_fetcher';
2020
import { extractFieldLabel } from '../../../common/fields_utils';
21+
2122
import type {
2223
VisTypeTimeseriesRequestHandlerContext,
2324
VisTypeTimeseriesRequestServices,
2425
VisTypeTimeseriesVisDataRequest,
2526
} from '../../types';
2627
import type { Panel } from '../../../common/types';
27-
import { getIntervalAndTimefield } from './get_interval_and_timefield';
2828

2929
export async function getTableData(
3030
requestContext: VisTypeTimeseriesRequestHandlerContext,
@@ -66,18 +66,6 @@ export async function getTableData(
6666
return panel.pivot_id;
6767
};
6868

69-
const buildSeriesMetaParams = async () => {
70-
let index = panelIndex;
71-
72-
/** This part of code is required to try to get the default timefield for string indices.
73-
* The rest of the functionality available for Kibana indexes should not be active **/
74-
if (!panel.use_kibana_indexes && index.indexPatternString) {
75-
index = await services.cachedIndexPatternFetcher(index.indexPatternString, true);
76-
}
77-
78-
return getIntervalAndTimefield(panel, index);
79-
};
80-
8169
const meta = {
8270
type: panel.type,
8371
uiRestrictions: capabilities.uiRestrictions,
@@ -93,7 +81,7 @@ export async function getTableData(
9381
panelIndex,
9482
capabilities,
9583
services.uiSettings,
96-
buildSeriesMetaParams
84+
() => services.buildSeriesMetaParams(panelIndex, Boolean(panel.use_kibana_indexes))
9785
);
9886

9987
const [resp] = await searchStrategy.search(requestContext, req, [

src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,16 @@ describe('dateHistogram(req, panel, series)', () => {
4747
get: async (key) => (key === UI_SETTINGS.HISTOGRAM_MAX_BARS ? 100 : 50),
4848
};
4949
buildSeriesMetaParams = jest.fn(async () => {
50-
return getIntervalAndTimefield(panel, indexPattern, series);
50+
return getIntervalAndTimefield(
51+
panel,
52+
indexPattern,
53+
{
54+
min: '2017-01-01T00:00:00Z',
55+
max: '2017-01-01T01:00:00Z',
56+
maxBuckets: 1000,
57+
},
58+
series
59+
);
5160
});
5261
});
5362

src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import { buildRequestBody } from './build_request_body';
10-
import { getIntervalAndTimefield } from '../get_interval_and_timefield';
1110

1211
import type { FetchedIndexPattern, Panel, Series } from '../../../../common/types';
1312
import type {
@@ -27,6 +26,7 @@ export async function getSeriesRequestParams(
2726
esShardTimeout,
2827
uiSettings,
2928
cachedIndexPatternFetcher,
29+
buildSeriesMetaParams,
3030
}: VisTypeTimeseriesRequestServices
3131
) {
3232
let seriesIndex = panelIndex;
@@ -35,18 +35,6 @@ export async function getSeriesRequestParams(
3535
seriesIndex = await cachedIndexPatternFetcher(series.series_index_pattern ?? '');
3636
}
3737

38-
const buildSeriesMetaParams = async () => {
39-
let index = seriesIndex;
40-
41-
/** This part of code is required to try to get the default timefield for string indices.
42-
* The rest of the functionality available for Kibana indexes should not be active **/
43-
if (!panel.use_kibana_indexes && index.indexPatternString) {
44-
index = await cachedIndexPatternFetcher(index.indexPatternString, true);
45-
}
46-
47-
return getIntervalAndTimefield(panel, index, series);
48-
};
49-
5038
const request = await buildRequestBody(
5139
req,
5240
panel,
@@ -55,7 +43,7 @@ export async function getSeriesRequestParams(
5543
seriesIndex,
5644
capabilities,
5745
uiSettings,
58-
buildSeriesMetaParams
46+
() => buildSeriesMetaParams(seriesIndex, Boolean(panel.use_kibana_indexes), series)
5947
);
6048

6149
return {

src/plugins/vis_type_timeseries/server/types.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@
66
* Side Public License, v 1.
77
*/
88

9-
import { Observable } from 'rxjs';
10-
import { SharedGlobalConfig } from 'kibana/server';
9+
import type { Observable } from 'rxjs';
10+
import type { SharedGlobalConfig } from 'kibana/server';
1111
import type { IRouter, IUiSettingsClient, KibanaRequest } from 'src/core/server';
1212
import type {
1313
DataRequestHandlerContext,
1414
EsQueryConfig,
1515
IndexPatternsService,
1616
} from '../../data/server';
17-
import type { VisPayload } from '../common/types';
17+
import type { Series, VisPayload } from '../common/types';
1818
import type { SearchStrategyRegistry } from './lib/search_strategies';
1919
import type { CachedIndexPatternFetcher } from './lib/search_strategies/lib/cached_index_pattern_fetcher';
20+
import type { FetchedIndexPattern } from '../common/types';
2021

2122
export type ConfigObservable = Observable<SharedGlobalConfig>;
2223

@@ -35,4 +36,13 @@ export interface VisTypeTimeseriesRequestServices {
3536
indexPatternsService: IndexPatternsService;
3637
searchStrategyRegistry: SearchStrategyRegistry;
3738
cachedIndexPatternFetcher: CachedIndexPatternFetcher;
39+
buildSeriesMetaParams: (
40+
index: FetchedIndexPattern,
41+
useKibanaIndexes: boolean,
42+
series?: Series
43+
) => Promise<{
44+
maxBars: number;
45+
timeField?: string;
46+
interval: string;
47+
}>;
3848
}

0 commit comments

Comments
 (0)