mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Transforms: Add support for timezone for date histogram pivot config (#155535)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
90eee76572
commit
90d3006678
6 changed files with 102 additions and 7 deletions
|
@ -30,6 +30,7 @@ export interface DateHistogramAgg {
|
|||
field: EsFieldName;
|
||||
calendar_interval: string;
|
||||
missing_bucket?: boolean;
|
||||
time_zone?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ interface GroupByDateHistogram extends GroupByConfigBase {
|
|||
field: EsFieldName;
|
||||
calendar_interval: string;
|
||||
missing_bucket?: boolean;
|
||||
time_zone?: string;
|
||||
}
|
||||
|
||||
interface GroupByHistogram extends GroupByConfigBase {
|
||||
|
|
|
@ -172,6 +172,7 @@ export const getRequestPayload = (
|
|||
date_histogram: {
|
||||
field: g.field,
|
||||
calendar_interval: g.calendar_interval,
|
||||
time_zone: g.time_zone,
|
||||
...getMissingBucketConfig(g),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { isValidTimeZone } from './time_zone_utils';
|
||||
|
||||
describe('Overrides utilities', () => {
|
||||
it('should return true for case-sensitive, acceptable timezones', async () => {
|
||||
expect(isValidTimeZone('America/New_York')).toEqual(true);
|
||||
expect(isValidTimeZone('America/Chicago')).toEqual(true);
|
||||
expect(isValidTimeZone('UTC')).toEqual(true);
|
||||
});
|
||||
it('should return false for invalid input', async () => {
|
||||
expect(isValidTimeZone('')).toEqual(false);
|
||||
expect(isValidTimeZone()).toEqual(false);
|
||||
expect(isValidTimeZone('Browser')).toEqual(false);
|
||||
expect(isValidTimeZone('EST')).toEqual(false);
|
||||
expect(isValidTimeZone('HST')).toEqual(false);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 moment from 'moment-timezone';
|
||||
import { isDefined } from '@kbn/ml-is-defined';
|
||||
|
||||
// Partial list of packages/core/ui-settings/core-ui-settings-common/src/timezones.ts
|
||||
export const ACCEPTED_TIMEZONES = new Set([
|
||||
...moment.tz
|
||||
.names()
|
||||
// We need to filter out some time zones, that moment.js knows about, but Elasticsearch
|
||||
// does not understand and would fail thus with a 400 bad request when using them.
|
||||
.filter((tz) => !['America/Nuuk', 'EST', 'HST', 'ROC', 'MST'].includes(tz)),
|
||||
]);
|
||||
|
||||
export const isValidTimeZone = (arg?: unknown): arg is string =>
|
||||
isDefined(arg) && typeof arg === 'string' && ACCEPTED_TIMEZONES.has(arg);
|
|
@ -14,6 +14,7 @@ import {
|
|||
EuiButton,
|
||||
EuiCheckbox,
|
||||
EuiCodeBlock,
|
||||
EuiComboBox,
|
||||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
|
@ -22,6 +23,9 @@ import {
|
|||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types';
|
||||
import { isDefined } from '@kbn/ml-is-defined';
|
||||
import { ACCEPTED_TIMEZONES, isValidTimeZone } from '../../../../common/time_zone_utils';
|
||||
import { AggName } from '../../../../../../common/types/aggregations';
|
||||
import { dictionaryToArray } from '../../../../../../common/types/common';
|
||||
|
||||
|
@ -109,6 +113,21 @@ export const PopoverForm: React.FC<Props> = ({ defaultData, otherAggNames, onCha
|
|||
isPivotGroupByConfigWithUiSupport(defaultData) ? defaultData.field : ''
|
||||
);
|
||||
const [interval, setInterval] = useState(getDefaultInterval(defaultData));
|
||||
|
||||
const TIMEZONE_OPTIONS = useMemo(
|
||||
() => [...ACCEPTED_TIMEZONES].map((value) => ({ value, label: value })),
|
||||
[]
|
||||
);
|
||||
|
||||
// Should default to time zone that user sets in json editor first
|
||||
const [selectedTimeZone, setSelectedTimeZone] = useState<Array<EuiComboBoxOptionOption<string>>>(
|
||||
isGroupByDateHistogram(defaultData) && isValidTimeZone(defaultData.time_zone)
|
||||
? [TIMEZONE_OPTIONS.find((v) => v.value === defaultData.time_zone)!]
|
||||
: []
|
||||
);
|
||||
|
||||
const timeZone = useMemo(() => selectedTimeZone[0]?.value, [selectedTimeZone]);
|
||||
|
||||
const [missingBucket, setMissingBucket] = useState(
|
||||
isPivotGroupByConfigWithUiSupport(defaultData) && defaultData.missing_bucket
|
||||
);
|
||||
|
@ -122,6 +141,10 @@ export const PopoverForm: React.FC<Props> = ({ defaultData, otherAggNames, onCha
|
|||
updatedItem.interval = interval;
|
||||
} else if (isGroupByDateHistogram(updatedItem) && interval !== undefined) {
|
||||
updatedItem.calendar_interval = interval;
|
||||
|
||||
if (isValidTimeZone(timeZone)) {
|
||||
updatedItem.time_zone = timeZone;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting to PivotGroupByConfig because TS would otherwise complain about the
|
||||
|
@ -169,9 +192,14 @@ export const PopoverForm: React.FC<Props> = ({ defaultData, otherAggNames, onCha
|
|||
(isGroupByDateHistogram(defaultData) || isGroupByHistogram(defaultData)) &&
|
||||
isIntervalValid(interval, defaultData.agg);
|
||||
|
||||
const timeZoneValid = isGroupByDateHistogram(defaultData) && isValidTimeZone(timeZone);
|
||||
let formValid = validAggName;
|
||||
if (formValid && (isGroupByDateHistogram(defaultData) || isGroupByHistogram(defaultData))) {
|
||||
formValid = isIntervalValid(interval, defaultData.agg);
|
||||
|
||||
if (isGroupByDateHistogram(defaultData) && isDefined(defaultData.time_zone)) {
|
||||
formValid = timeZoneValid;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -225,13 +253,11 @@ export const PopoverForm: React.FC<Props> = ({ defaultData, otherAggNames, onCha
|
|||
)}
|
||||
{(isGroupByDateHistogram(defaultData) || isGroupByHistogram(defaultData)) && (
|
||||
<EuiFormRow
|
||||
error={
|
||||
!validInterval && [
|
||||
i18n.translate('xpack.transform.groupBy.popoverForm.intervalError', {
|
||||
defaultMessage: 'Invalid interval.',
|
||||
}),
|
||||
]
|
||||
}
|
||||
error={[
|
||||
i18n.translate('xpack.transform.groupBy.popoverForm.intervalError', {
|
||||
defaultMessage: 'Invalid interval.',
|
||||
}),
|
||||
]}
|
||||
isInvalid={!validInterval}
|
||||
label={i18n.translate('xpack.transform.groupBy.popoverForm.intervalLabel', {
|
||||
defaultMessage: 'Interval',
|
||||
|
@ -263,6 +289,28 @@ export const PopoverForm: React.FC<Props> = ({ defaultData, otherAggNames, onCha
|
|||
</>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
{isGroupByDateHistogram(defaultData) && isDefined(defaultData.time_zone) ? (
|
||||
<EuiFormRow
|
||||
error={i18n.translate('xpack.transform.groupBy.popoverForm.timeZoneError', {
|
||||
defaultMessage: 'Invalid time zone.',
|
||||
})}
|
||||
isInvalid={!timeZoneValid}
|
||||
label={i18n.translate('xpack.transform.groupBy.popoverForm.timeZoneLabel', {
|
||||
defaultMessage: 'Time zone',
|
||||
})}
|
||||
>
|
||||
<EuiComboBox
|
||||
options={TIMEZONE_OPTIONS}
|
||||
onChange={(opt) => setSelectedTimeZone(opt)}
|
||||
selectedOptions={selectedTimeZone}
|
||||
aria-label={i18n.translate('xpack.transform.groupBy.popoverForm.timeZoneAriaLabel', {
|
||||
defaultMessage: 'Time zone',
|
||||
})}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
) : null}
|
||||
|
||||
{!isUnsupportedAgg && (
|
||||
<EuiFormRow
|
||||
helpText={
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue