[ML] Fixes job validation for nested time fields. (#24137)

This fixes an issue where job validation would return that a date field was invalid for nested date fields.
This commit is contained in:
Walter Rafelsberger 2018-10-17 17:33:29 +02:00 committed by GitHub
parent 82e97dd8e0
commit e59c1a11d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 20 deletions

View file

@ -0,0 +1 @@
{"fields":{"metadata.timestamp":{"date":{"type":"date","searchable":true,"aggregatable":true}}}}

View file

@ -8,9 +8,10 @@
import _ from 'lodash';
import expect from 'expect.js';
import { validateTimeRange } from '../validate_time_range';
import { isValidTimeField, validateTimeRange } from '../validate_time_range';
import mockTimeField from './mock_time_field';
import mockTimeFieldNested from './mock_time_field_nested';
import mockTimeRange from './mock_time_range';
const mockSearchResponse = {
@ -26,6 +27,58 @@ const callWithRequestFactory = (resp) => {
};
};
function getMinimalValidJob() {
return {
analysis_config: {
bucket_span: '15m',
detectors: [],
influencers: []
},
data_description: { time_field: '@timestamp' },
datafeed_config: {
indices: []
}
};
}
describe('ML - isValidTimeField', () => {
it('called without job config argument triggers Promise rejection', (done) => {
isValidTimeField(callWithRequestFactory(mockSearchResponse)).then(
() => done(new Error('Promise should not resolve for this test without job argument.')),
() => done()
);
});
it('time_field `@timestamp`', (done) => {
isValidTimeField(callWithRequestFactory(mockSearchResponse), getMinimalValidJob()).then(
(valid) => {
expect(valid).to.be(true);
done();
},
() => done(new Error('isValidTimeField Promise failed for time_field `@timestamp`.'))
);
});
it('time_field `metadata.timestamp`', (done) => {
const mockJobConfigNestedDate = getMinimalValidJob();
mockJobConfigNestedDate.data_description.time_field = 'metadata.timestamp';
const mockSearchResponseNestedDate = {
fieldCaps: mockTimeFieldNested,
search: mockTimeRange
};
isValidTimeField(callWithRequestFactory(mockSearchResponseNestedDate), mockJobConfigNestedDate).then(
(valid) => {
expect(valid).to.be(true);
done();
},
() => done(new Error('isValidTimeField Promise failed for time_field `metadata.timestamp`.'))
);
});
});
describe('ML - validateTimeRange', () => {
it('called without arguments', (done) => {
@ -66,23 +119,11 @@ describe('ML - validateTimeRange', () => {
);
});
const minimumValidJob = {
analysis_config: {
bucket_span: '15m',
detectors: [],
influencers: []
},
data_description: { time_field: '@timestamp' },
datafeed_config: {
indices: []
}
};
it('invalid time field', () => {
const mockSearchResponseInvalid = _.cloneDeep(mockSearchResponse);
mockSearchResponseInvalid.fieldCaps = undefined;
const duration = { start: 0, end: 1 };
return validateTimeRange(callWithRequestFactory(mockSearchResponseInvalid), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponseInvalid), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_field_invalid']);
@ -91,7 +132,7 @@ describe('ML - validateTimeRange', () => {
});
it('too short time range, 25x bucket span is less than 2h', () => {
const jobShortTimeRange = _.cloneDeep(minimumValidJob);
const jobShortTimeRange = getMinimalValidJob();
jobShortTimeRange.analysis_config.bucket_span = '1s';
const duration = { start: 0, end: 1 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), jobShortTimeRange, duration).then(
@ -104,7 +145,7 @@ describe('ML - validateTimeRange', () => {
it('too short time range, 25x bucket span is more than 2h', () => {
const duration = { start: 0, end: 1 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_range_short']);
@ -114,7 +155,7 @@ describe('ML - validateTimeRange', () => {
it('time range between 2h and 25x bucket span', () => {
const duration = { start: 0, end: 8000000 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_range_short']);
@ -124,7 +165,7 @@ describe('ML - validateTimeRange', () => {
it('valid time range', () => {
const duration = { start: 0, end: 100000000 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['success_time_range']);
@ -134,7 +175,7 @@ describe('ML - validateTimeRange', () => {
it('invalid time range, start time is before the UNIX epoch', () => {
const duration = { start: -1, end: 100000000 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_range_before_epoch']);

View file

@ -25,7 +25,9 @@ export async function isValidTimeField(callWithRequest, job) {
index,
fields: [timeField]
});
const fieldType = _.get(fieldCaps, `fields.${timeField}.date.type`);
// get the field's type with the following notation
// because a nested field could contain dots and confuse _.get
const fieldType = _.get(fieldCaps, `fields['${timeField}'].date.type`);
return fieldType === ES_FIELD_TYPES.DATE;
}