mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Fix invisible filters caused by missing index pattern (#14131)
"invisible filters" occur when the mapping chain throws an error. If a single filter throws an error, the entire chain rejects. As a result, not even the valid filters appear in the filter bar because they never get added to the scope. However the filters still exist in app state and still get sent with each search request. The most common error occurs when the filter's meta.index property points to a non-existing index pattern. Since this property is only used for looking up field formatters and it is not essential for a working filter, we now fall back on raw values instead of failing if the index pattern is not found. See the PR this one replaces for discussion about other solutions we tried and why we chose to go this route.
This commit is contained in:
parent
3e522c35e3
commit
e09d7ad1f9
7 changed files with 88 additions and 117 deletions
|
@ -1,69 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { FilterBarLibMapScriptProvider } from 'ui/filter_bar/lib/map_script';
|
||||
|
||||
describe('Filter Bar Directive', function () {
|
||||
describe('mapScript()', function () {
|
||||
let mapScript;
|
||||
let $rootScope;
|
||||
|
||||
beforeEach(ngMock.module(
|
||||
'kibana',
|
||||
'kibana/courier',
|
||||
function ($provide) {
|
||||
$provide.service('courier', require('fixtures/mock_courier'));
|
||||
}
|
||||
));
|
||||
|
||||
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
|
||||
$rootScope = _$rootScope_;
|
||||
mapScript = Private(FilterBarLibMapScriptProvider);
|
||||
}));
|
||||
|
||||
it('should return the key and value for matching filters', function (done) {
|
||||
const filter = {
|
||||
meta: { index: 'logstash-*', field: 'script number' },
|
||||
script: { script: { inline: 'doc["script number"].value * 5', params: { value: 35 } } }
|
||||
};
|
||||
mapScript(filter).then(function (result) {
|
||||
expect(result).to.have.property('key', 'script number');
|
||||
expect(result).to.have.property('value', '35');
|
||||
done();
|
||||
});
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
it('should return undefined for none matching', function (done) {
|
||||
const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } } };
|
||||
mapScript(filter).catch(function (result) {
|
||||
expect(result).to.be(filter);
|
||||
done();
|
||||
});
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
it('should return a value for a range/histogram filter from a scripted field', (done) => {
|
||||
const filter = {
|
||||
meta: {
|
||||
index: 'logstash-*',
|
||||
formattedValue: '1,000.00 to 2,000.00',
|
||||
field: 'script number'
|
||||
},
|
||||
script: {
|
||||
script: {
|
||||
params: {
|
||||
gte: 1000,
|
||||
lt: 2000,
|
||||
value: '>=1,000.00 <2,000.00'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mapScript(filter).then((result) => {
|
||||
expect(result).to.have.property('value', filter.meta.formattedValue);
|
||||
done();
|
||||
});
|
||||
$rootScope.$apply();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,7 +9,6 @@ import { FilterBarLibMapMissingProvider } from './map_missing';
|
|||
import { FilterBarLibMapQueryStringProvider } from './map_query_string';
|
||||
import { FilterBarLibMapGeoBoundingBoxProvider } from './map_geo_bounding_box';
|
||||
import { FilterBarLibMapGeoPolygonProvider } from './map_geo_polygon';
|
||||
import { FilterBarLibMapScriptProvider } from './map_script';
|
||||
import { FilterBarLibMapDefaultProvider } from './map_default';
|
||||
|
||||
export function FilterBarLibMapFilterProvider(Promise, Private) {
|
||||
|
@ -42,7 +41,6 @@ export function FilterBarLibMapFilterProvider(Promise, Private) {
|
|||
Private(FilterBarLibMapQueryStringProvider),
|
||||
Private(FilterBarLibMapGeoBoundingBoxProvider),
|
||||
Private(FilterBarLibMapGeoPolygonProvider),
|
||||
Private(FilterBarLibMapScriptProvider),
|
||||
Private(FilterBarLibMapDefaultProvider),
|
||||
];
|
||||
|
||||
|
|
|
@ -1,20 +1,38 @@
|
|||
import _ from 'lodash';
|
||||
import { SavedObjectNotFound } from '../../errors';
|
||||
|
||||
export function FilterBarLibMapGeoBoundingBoxProvider(Promise, courier) {
|
||||
return function (filter) {
|
||||
if (filter.geo_bounding_box) {
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index).then(function (indexPattern) {
|
||||
function getParams(indexPattern) {
|
||||
const type = 'geo_bounding_box';
|
||||
const key = _.keys(filter.geo_bounding_box)
|
||||
.filter(key => key !== 'ignore_unmapped')[0];
|
||||
const field = indexPattern.fields.byName[key];
|
||||
const params = filter.geo_bounding_box[key];
|
||||
const topLeft = field.format.convert(params.top_left);
|
||||
const bottomRight = field.format.convert(params.bottom_right);
|
||||
|
||||
// Sometimes a filter will end up with an invalid index param. This could happen for a lot of reasons,
|
||||
// for example a user might manually edit the url or the index pattern's ID might change due to
|
||||
// external factors e.g. a reindex. We only need the index in order to grab the field formatter, so we fallback
|
||||
// on displaying the raw value if the index is invalid.
|
||||
const topLeft = indexPattern
|
||||
? indexPattern.fields.byName[key].format.convert(params.top_left)
|
||||
: JSON.stringify(params.top_left);
|
||||
const bottomRight = indexPattern
|
||||
? indexPattern.fields.byName[key].format.convert(params.bottom_right)
|
||||
: JSON.stringify(params.bottom_right);
|
||||
const value = topLeft + ' to ' + bottomRight;
|
||||
return { type, key, value, params };
|
||||
}
|
||||
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index)
|
||||
.then(getParams)
|
||||
.catch((error) => {
|
||||
if (error instanceof SavedObjectNotFound) {
|
||||
return getParams();
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return Promise.reject(filter);
|
||||
|
|
|
@ -1,19 +1,37 @@
|
|||
import _ from 'lodash';
|
||||
import { SavedObjectNotFound } from '../../errors';
|
||||
|
||||
export function FilterBarLibMapGeoPolygonProvider(Promise, courier) {
|
||||
return function (filter) {
|
||||
if (filter.geo_polygon) {
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index).then(function (indexPattern) {
|
||||
function getParams(indexPattern) {
|
||||
const type = 'geo_polygon';
|
||||
const key = _.keys(filter.geo_polygon)
|
||||
.filter(key => key !== 'ignore_unmapped')[0];
|
||||
const field = indexPattern.fields.byName[key];
|
||||
const params = filter.geo_polygon[key];
|
||||
const points = params.points.map((point) => field.format.convert(point));
|
||||
|
||||
// Sometimes a filter will end up with an invalid index param. This could happen for a lot of reasons,
|
||||
// for example a user might manually edit the url or the index pattern's ID might change due to
|
||||
// external factors e.g. a reindex. We only need the index in order to grab the field formatter, so we fallback
|
||||
// on displaying the raw value if the index is invalid.
|
||||
const points = params.points.map((point) => {
|
||||
return indexPattern
|
||||
? indexPattern.fields.byName[key].format.convert(point)
|
||||
: JSON.stringify(point);
|
||||
});
|
||||
const value = points.join(', ');
|
||||
return { type, key, value, params };
|
||||
}
|
||||
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index)
|
||||
.then(getParams)
|
||||
.catch((error) => {
|
||||
if (error instanceof SavedObjectNotFound) {
|
||||
return getParams();
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return Promise.reject(filter);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash';
|
||||
import { SavedObjectNotFound } from '../../errors';
|
||||
|
||||
export function FilterBarLibMapPhraseProvider(Promise, courier) {
|
||||
return function (filter) {
|
||||
|
@ -7,16 +8,29 @@ export function FilterBarLibMapPhraseProvider(Promise, courier) {
|
|||
return Promise.reject(filter);
|
||||
}
|
||||
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index).then(function (indexPattern) {
|
||||
function getParams(indexPattern) {
|
||||
const type = 'phrase';
|
||||
const key = isScriptedPhraseFilter ? filter.meta.field : Object.keys(filter.query.match)[0];
|
||||
const field = indexPattern.fields.byName[key];
|
||||
const params = isScriptedPhraseFilter ? filter.script.script.params : filter.query.match[key];
|
||||
const query = isScriptedPhraseFilter ? params.value : params.query;
|
||||
const value = field.format.convert(query);
|
||||
|
||||
// Sometimes a filter will end up with an invalid index param. This could happen for a lot of reasons,
|
||||
// for example a user might manually edit the url or the index pattern's ID might change due to
|
||||
// external factors e.g. a reindex. We only need the index in order to grab the field formatter, so we fallback
|
||||
// on displaying the raw value if the index is invalid.
|
||||
const value = indexPattern ? indexPattern.fields.byName[key].format.convert(query) : query;
|
||||
return { type, key, value, params };
|
||||
}
|
||||
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index)
|
||||
.then(getParams)
|
||||
.catch((error) => {
|
||||
if (error instanceof SavedObjectNotFound) {
|
||||
return getParams();
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { has, get } from 'lodash';
|
||||
import { SavedObjectNotFound } from '../../errors';
|
||||
|
||||
export function FilterBarLibMapRangeProvider(Promise, courier) {
|
||||
return function (filter) {
|
||||
|
@ -7,13 +8,9 @@ export function FilterBarLibMapRangeProvider(Promise, courier) {
|
|||
return Promise.reject(filter);
|
||||
}
|
||||
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index)
|
||||
.then(function (indexPattern) {
|
||||
function getParams(indexPattern) {
|
||||
const type = 'range';
|
||||
const key = isScriptedRangeFilter ? filter.meta.field : Object.keys(filter.range)[0];
|
||||
const convert = indexPattern.fields.byName[key].format.getConverterFor('text');
|
||||
const params = isScriptedRangeFilter ? filter.script.script.params : filter.range[key];
|
||||
|
||||
let left = has(params, 'gte') ? params.gte : params.gt;
|
||||
|
@ -22,9 +19,28 @@ export function FilterBarLibMapRangeProvider(Promise, courier) {
|
|||
let right = has(params, 'lte') ? params.lte : params.lt;
|
||||
if (right == null) right = Infinity;
|
||||
|
||||
const value = `${convert(left)} to ${convert(right)}`;
|
||||
// Sometimes a filter will end up with an invalid index param. This could happen for a lot of reasons,
|
||||
// for example a user might manually edit the url or the index pattern's ID might change due to
|
||||
// external factors e.g. a reindex. We only need the index in order to grab the field formatter, so we fallback
|
||||
// on displaying the raw value if the index is invalid.
|
||||
let value = `${left} to ${right}`;
|
||||
if (indexPattern) {
|
||||
const convert = indexPattern.fields.byName[key].format.getConverterFor('text');
|
||||
value = `${convert(left)} to ${convert(right)}`;
|
||||
}
|
||||
|
||||
return { type, key, value, params };
|
||||
}
|
||||
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index)
|
||||
.then(getParams)
|
||||
.catch((error) => {
|
||||
if (error instanceof SavedObjectNotFound) {
|
||||
return getParams();
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
|
||||
};
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
export function FilterBarLibMapScriptProvider(Promise, courier) {
|
||||
return function (filter) {
|
||||
if (filter.script) {
|
||||
return courier
|
||||
.indexPatterns
|
||||
.get(filter.meta.index).then(function (indexPattern) {
|
||||
const type = 'scripted';
|
||||
const key = filter.meta.field;
|
||||
const field = indexPattern.fields.byName[key];
|
||||
|
||||
let value;
|
||||
if (filter.meta.formattedValue) {
|
||||
value = filter.meta.formattedValue;
|
||||
} else {
|
||||
value = filter.script.script.params.value;
|
||||
value = field.format.convert(value);
|
||||
}
|
||||
|
||||
return { type, key, value };
|
||||
});
|
||||
}
|
||||
return Promise.reject(filter);
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue