[6.x] [@kbn/datemath] improve types (#24671) (#24801)

Backports the following commits to 6.x:
 - [@kbn/datemath] improve types  (#24671)
This commit is contained in:
Spencer 2018-10-29 20:41:39 -07:00 committed by GitHub
parent ecc24b27f8
commit 627a422de2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 26 deletions

View file

@ -17,13 +17,29 @@
* under the License.
*/
declare module '@kbn/datemath' {
const dateMath: {
parse: any;
unitsMap: any;
units: string[];
unitsAsc: string[];
unitsDesc: string[];
import moment from 'moment';
export type Unit = 'ms' | 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y';
declare const datemath: {
unitsMap: {
[k in Unit]: {
weight: number;
type: 'calendar' | 'fixed' | 'mixed';
base: number;
}
};
export default dateMath;
}
units: Unit[];
unitsAsc: Unit[];
unitsDesc: Unit[];
parse(
input: string,
options?: {
roundUp?: boolean;
forceNow?: boolean;
momentInstance?: typeof moment;
}
): moment.Moment | undefined;
};
export default datemath;

View file

@ -25,10 +25,10 @@ const unitsMap = {
m: { weight: 3, type: 'mixed', base: 1000 * 60 },
h: { weight: 4, type: 'mixed', base: 1000 * 60 * 60 },
d: { weight: 5, type: 'mixed', base: 1000 * 60 * 60 * 24 },
w: { weight: 6, type: 'calendar' },
M: { weight: 7, type: 'calendar' },
w: { weight: 6, type: 'calendar', base: NaN },
M: { weight: 7, type: 'calendar', base: NaN },
// q: { weight: 8, type: 'calendar' }, // TODO: moment duration does not support quarter
y: { weight: 9, type: 'calendar' },
y: { weight: 9, type: 'calendar', base: NaN },
};
const units = Object.keys(unitsMap).sort((a, b) => unitsMap[b].weight - unitsMap[a].weight);
const unitsDesc = [...units];

View file

@ -17,6 +17,6 @@
* under the License.
*/
export { parseEsInterval } from './parse_es_interval';
export { parseEsInterval, ParsedInterval } from './parse_es_interval';
export { InvalidEsCalendarIntervalError } from './invalid_es_calendar_interval_error';
export { InvalidEsIntervalFormatError } from './invalid_es_interval_format_error';

View file

@ -17,11 +17,13 @@
* under the License.
*/
import { Unit } from '@kbn/datemath';
export class InvalidEsCalendarIntervalError extends Error {
constructor(
public readonly interval: string,
public readonly value: number,
public readonly unit: string,
public readonly unit: Unit,
public readonly type: string
) {
super(`Invalid calendar interval: ${interval}, value must be 1`);

View file

@ -17,7 +17,7 @@
* under the License.
*/
import dateMath from '@kbn/datemath';
import dateMath, { Unit } from '@kbn/datemath';
import { InvalidEsCalendarIntervalError } from './invalid_es_calendar_interval_error';
import { InvalidEsIntervalFormatError } from './invalid_es_interval_format_error';
@ -26,6 +26,8 @@ const ES_INTERVAL_STRING_REGEX = new RegExp(
'^([1-9][0-9]*)\\s*(' + dateMath.units.join('|') + ')$'
);
export type ParsedInterval = ReturnType<typeof parseEsInterval>;
/**
* Extracts interval properties from an ES interval string. Disallows unrecognized interval formats
* and fractional values. Converts some intervals from "calendar" to "fixed" when the number of
@ -37,7 +39,7 @@ const ES_INTERVAL_STRING_REGEX = new RegExp(
* | -------- | ---------------- | ------------------- |
* | ms | fixed | fixed |
* | s | fixed | fixed |
* | m | fixed | fixed |
* | m | calendar | fixed |
* | h | calendar | fixed |
* | d | calendar | fixed |
* | w | calendar | N/A - disallowed |
@ -45,7 +47,7 @@ const ES_INTERVAL_STRING_REGEX = new RegExp(
* | y | calendar | N/A - disallowed |
*
*/
export function parseEsInterval(interval: string): { value: number; unit: string; type: string } {
export function parseEsInterval(interval: string) {
const matches = String(interval)
.trim()
.match(ES_INTERVAL_STRING_REGEX);
@ -54,9 +56,9 @@ export function parseEsInterval(interval: string): { value: number; unit: string
throw new InvalidEsIntervalFormatError(interval);
}
const value = matches && parseFloat(matches[1]);
const unit = matches && matches[2];
const type = unit && dateMath.unitsMap[unit].type;
const value = parseFloat(matches[1]);
const unit = matches[2] as Unit;
const type = dateMath.unitsMap[unit].type;
if (type === 'calendar' && value !== 1) {
throw new InvalidEsCalendarIntervalError(interval, value, unit, type);
@ -65,6 +67,9 @@ export function parseEsInterval(interval: string): { value: number; unit: string
return {
value,
unit,
type: (type === 'mixed' && value === 1) || type === 'calendar' ? 'calendar' : 'fixed',
type:
(type === 'mixed' && value === 1) || type === 'calendar'
? ('calendar' as 'calendar')
: ('fixed' as 'fixed'),
};
}

View file

@ -48,6 +48,12 @@ describe('leastCommonInterval', () => {
expect(() => {
leastCommonInterval('60 s', '1m');
}).toThrowError();
expect(() => {
leastCommonInterval('1m', '2m');
}).toThrowError();
expect(() => {
leastCommonInterval('1h', '2h');
}).toThrowError();
expect(() => {
leastCommonInterval('1d', '7d');
}).toThrowError();

View file

@ -25,8 +25,8 @@ import { parseEsInterval } from '../../utils/parse_es_interval';
* Finds the lowest common interval between two given ES date histogram intervals
* in the format of (value)(unit)
*
* - `ms, s` units are fixed-length intervals
* - `m, h, d` units are fixed-length intervals when value > 1 (i.e. 2m, 24h, 7d),
* - `ms` units are fixed-length intervals
* - `s, m, h, d` units are fixed-length intervals when value > 1 (i.e. 2m, 24h, 7d),
* but calendar interval when value === 1
* - `w, M, q, y` units are calendar intervals and do not support multiple, aka
* value must === 1
@ -51,7 +51,7 @@ export function leastCommonInterval(a: string, b: string): string {
}
// If intervals are calendar units, pick the larger one (calendar value is always 1)
if (aInt.type === 'calendar') {
if (aInt.type === 'calendar' || bInt.type === 'calendar') {
return aUnit.weight > bUnit.weight ? `${aInt.value}${aInt.unit}` : `${bInt.value}${bInt.unit}`;
}
@ -68,8 +68,11 @@ export function leastCommonInterval(a: string, b: string): string {
return a.replace(/\s/g, '');
}
// Otherwise find the biggest unit that divides evenly
const lcmUnit = unitsDesc.find(unit => unitsMap[unit].base && lcmMs % unitsMap[unit].base === 0);
// Otherwise find the biggest non-calendar unit that divides evenly
const lcmUnit = unitsDesc.find(unit => {
const unitInfo = unitsMap[unit];
return !!(unitInfo.type !== 'calendar' && lcmMs % unitInfo.base === 0);
});
// Throw error in case we couldn't divide evenly, theoretically we never get here as everything is
// divisible by 1 millisecond