Remove (most of) Angular dependecies from filter bar (#35544) (#36159)

* Cleaned up usage of angular dependencies from apply filters \ filter bar

* Cleaned up usage of angular dependencies from  exact time filter

* Removed unused Private from EventsProvider

* Deleted unused push_filter

* Changed push filters not to be a provider

* Renamed mapper functions
This commit is contained in:
Liza Katz 2019-05-07 20:52:44 +03:00 committed by GitHub
parent f8cfc9e771
commit 650c7d6d8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 404 additions and 776 deletions

View file

@ -20,10 +20,10 @@
import sinon from 'sinon';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
export default function (Private, Promise) {
export default function (Private) {
const indexPatterns = Private(FixturesStubbedLogstashIndexPatternProvider);
const getIndexPatternStub = sinon.stub()
.returns(Promise.resolve(indexPatterns));
.resolves(indexPatterns);
return {
get: getIndexPatternStub,

View file

@ -21,17 +21,16 @@ import 'ngreact';
import { uiModules } from '../modules';
import template from './directive.html';
import { ApplyFiltersPopover } from './apply_filters_popover';
import { FilterBarLibMapAndFlattenFiltersProvider } from '../filter_bar/lib/map_and_flatten_filters';
import { mapAndFlattenFilters } from '../filter_bar/lib/map_and_flatten_filters';
import { wrapInI18nContext } from 'ui/i18n';
const app = uiModules.get('app/kibana', ['react']);
app.directive('applyFiltersPopoverComponent', (reactDirective) => {
return reactDirective(ApplyFiltersPopover);
return reactDirective(wrapInI18nContext(ApplyFiltersPopover));
});
app.directive('applyFiltersPopover', (reactDirective, Private) => {
const mapAndFlattenFilters = Private(FilterBarLibMapAndFlattenFiltersProvider);
app.directive('applyFiltersPopover', (indexPatterns) => {
return {
template,
restrict: 'E',
@ -47,7 +46,7 @@ app.directive('applyFiltersPopover', (reactDirective, Private) => {
// popover, because it has to reset its state whenever the new filters change. Setting a `key`
// property on the component accomplishes this due to how React handles the `key` property.
$scope.$watch('filters', filters => {
mapAndFlattenFilters(filters).then(mappedFilters => {
mapAndFlattenFilters(indexPatterns, filters).then(mappedFilters => {
$scope.state = {
filters: mappedFilters,
key: Date.now(),

View file

@ -30,7 +30,7 @@ import { createLegacyClass } from './utils/legacy_class';
const location = 'EventEmitter';
export function EventsProvider(Private, Promise) {
export function EventsProvider(Promise) {
createLegacyClass(Events).inherits(SimpleEmitter);
function Events() {
Events.Super.call(this);

View file

@ -25,6 +25,8 @@ import MockState from 'fixtures/mock_state';
import { FilterBarQueryFilterProvider } from '../query_filter';
describe('add filters', function () {
require('test_utils/no_digest_promises').activateForSuite();
let filters;
let queryFilter;
let $rootScope;
@ -73,44 +75,33 @@ describe('add filters', function () {
});
describe('adding filters', function () {
it('should add filters to appState', function () {
$rootScope.$digest();
queryFilter.addFilters(filters);
$rootScope.$digest();
it('should add filters to appState', async function () {
await queryFilter.addFilters(filters);
expect(appState.filters.length).to.be(3);
expect(globalState.filters.length).to.be(0);
});
it('should add filters to globalState', function () {
$rootScope.$digest();
queryFilter.addFilters(filters, true);
$rootScope.$digest();
it('should add filters to globalState', async function () {
await queryFilter.addFilters(filters, true);
expect(appState.filters.length).to.be(0);
expect(globalState.filters.length).to.be(3);
});
it('should accept a single filter', function () {
$rootScope.$digest();
queryFilter.addFilters(filters[0]);
$rootScope.$digest();
it('should accept a single filter', async function () {
await queryFilter.addFilters(filters[0]);
expect(appState.filters.length).to.be(1);
expect(globalState.filters.length).to.be(0);
});
it('should allow overwriting a positive filter by a negated one', () => {
$rootScope.$digest();
it('should allow overwriting a positive filter by a negated one', async function () {
// Add negate: false version of the filter
const filter = _.cloneDeep(filters[0]);
filter.meta.negate = false;
queryFilter.addFilters(filter);
await queryFilter.addFilters(filter);
$rootScope.$digest();
expect(appState.filters.length).to.be(1);
expect(appState.filters[0]).to.eql(filter);
@ -119,22 +110,21 @@ describe('add filters', function () {
const negatedFilter = _.cloneDeep(filters[0]);
negatedFilter.meta.negate = true;
queryFilter.addFilters(negatedFilter);
await queryFilter.addFilters(negatedFilter);
$rootScope.$digest();
// The negated filter should overwrite the positive one
expect(appState.filters.length).to.be(1);
expect(appState.filters[0]).to.eql(negatedFilter);
});
it('should allow overwriting a negated filter by a positive one', () => {
$rootScope.$digest();
it('should allow overwriting a negated filter by a positive one', async function () {
// Add negate: true version of the same filter
const negatedFilter = _.cloneDeep(filters[0]);
negatedFilter.meta.negate = true;
queryFilter.addFilters(negatedFilter);
await queryFilter.addFilters(negatedFilter);
$rootScope.$digest();
// The negated filter should overwrite the positive one
expect(appState.filters.length).to.be(1);
expect(appState.filters[0]).to.eql(negatedFilter);
@ -143,18 +133,24 @@ describe('add filters', function () {
const filter = _.cloneDeep(filters[0]);
filter.meta.negate = false;
queryFilter.addFilters(filter);
await queryFilter.addFilters(filter);
$rootScope.$digest();
expect(appState.filters.length).to.be(1);
expect(appState.filters[0]).to.eql(filter);
});
it('should fire the update and fetch events', function () {
it('should fire the update and fetch events', async function () {
const emitSpy = sinon.spy(queryFilter, 'emit');
const awaitFetch = new Promise(resolve => {
queryFilter.on('fetch', () => {
resolve();
});
});
// set up the watchers, add new filters, and crank the digest loop
$rootScope.$digest();
queryFilter.addFilters(filters);
await queryFilter.addFilters(filters);
$rootScope.$digest();
// updates should trigger state saves
@ -162,44 +158,44 @@ describe('add filters', function () {
expect(globalState.save.callCount).to.be(1);
// this time, events should be emitted
await awaitFetch;
expect(emitSpy.callCount).to.be(2);
expect(emitSpy.firstCall.args[0]).to.be('update');
expect(emitSpy.secondCall.args[0]).to.be('fetch');
});
});
describe('filter reconciliation', function () {
it('should de-dupe appState filters being added', function () {
it('should de-dupe appState filters being added', async function () {
const newFilter = _.cloneDeep(filters[1]);
appState.filters = filters;
$rootScope.$digest();
expect(appState.filters.length).to.be(3);
queryFilter.addFilters(newFilter);
await queryFilter.addFilters(newFilter);
$rootScope.$digest();
expect(appState.filters.length).to.be(3);
});
it('should de-dupe globalState filters being added', function () {
it('should de-dupe globalState filters being added', async function () {
const newFilter = _.cloneDeep(filters[1]);
globalState.filters = filters;
$rootScope.$digest();
expect(globalState.filters.length).to.be(3);
queryFilter.addFilters(newFilter, true);
await queryFilter.addFilters(newFilter, true);
$rootScope.$digest();
expect(globalState.filters.length).to.be(3);
});
it('should mutate global filters on appState filter changes', function () {
it('should mutate global filters on appState filter changes', async function () {
const idx = 1;
globalState.filters = filters;
$rootScope.$digest();
const appFilter = _.cloneDeep(filters[idx]);
appFilter.meta.negate = true;
queryFilter.addFilters(appFilter);
await queryFilter.addFilters(appFilter);
$rootScope.$digest();
const res = queryFilter.getFilters();

View file

@ -1,87 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarPushFilterProvider } from '../push_filter';
describe('Filter Bar pushFilter()', function () {
let pushFilterFn;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
pushFilterFn = Private(FilterBarPushFilterProvider);
}));
it('is a function that returns a function', function () {
expect(pushFilterFn).to.be.a(Function);
expect(pushFilterFn({})).to.be.a(Function);
});
it('throws an error if passed something besides an object', function () {
expect(pushFilterFn).withArgs(true).to.throwError();
});
describe('pushFilter($state)()', function () {
let $state;
let pushFilter;
let filter;
beforeEach(ngMock.inject(function () {
$state = { $newFilters: [] };
pushFilter = pushFilterFn($state);
filter = { query: { query_string: { query: '' } } };
}));
it('should create the filters property it needed', function () {
const altState = {};
pushFilterFn(altState)(filter);
expect(altState.$newFilters).to.be.an(Array);
});
it('should replace the filters property instead of modifying it', function () {
// If we push directly instead of using pushFilter a $watch('filters') does not trigger
let oldFilters;
oldFilters = $state.$newFilters;
$state.$newFilters.push(filter);
expect($state.$newFilters).to.equal(oldFilters); // Same object
oldFilters = $state.$newFilters;
pushFilter(filter);
expect($state.$newFilters).to.not.equal(oldFilters); // New object!
});
it('should add meta data to the filter', function () {
pushFilter(filter, true, 'myIndex');
expect($state.$newFilters[0].meta).to.be.an(Object);
expect($state.$newFilters[0].meta.negate).to.be(true);
expect($state.$newFilters[0].meta.index).to.be('myIndex');
pushFilter(filter, false, 'myIndex');
expect($state.$newFilters[0].meta.negate).to.be(false);
});
});
});

View file

@ -19,24 +19,20 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibExtractTimeFilterProvider } from '../extract_time_filter';
import { extractTimeFilter } from '../extract_time_filter';
import IndexPatternMock from 'fixtures/mock_index_patterns';
describe('Filter Bar Directive', function () {
describe('extractTimeFilter()', function () {
let extractTimeFilter;
let $rootScope;
let mockIndexPatterns;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
'kibana/courier'
));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
extractTimeFilter = Private(FilterBarLibExtractTimeFilterProvider);
$rootScope = _$rootScope_;
beforeEach(ngMock.inject(function (Private) {
mockIndexPatterns = Private(IndexPatternMock);
}));
it('should return the matching filter for the default time field', function (done) {
@ -44,11 +40,10 @@ describe('Filter Bar Directive', function () {
{ meta: { index: 'logstash-*' }, query: { match: { _type: { query: 'apache', type: 'phrase' } } } },
{ meta: { index: 'logstash-*' }, range: { 'time': { gt: 1388559600000, lt: 1388646000000 } } }
];
extractTimeFilter(filters).then(function (filter) {
extractTimeFilter(mockIndexPatterns, filters).then(function (filter) {
expect(filter).to.eql(filters[1]);
done();
});
$rootScope.$apply();
});
it('should not return the non-matching filter for the default time field', function (done) {
@ -56,11 +51,10 @@ describe('Filter Bar Directive', function () {
{ meta: { index: 'logstash-*' }, query: { match: { _type: { query: 'apache', type: 'phrase' } } } },
{ meta: { index: 'logstash-*' }, range: { '@timestamp': { gt: 1388559600000, lt: 1388646000000 } } }
];
extractTimeFilter(filters).then(function (filter) {
extractTimeFilter(mockIndexPatterns, filters).then(function (filter) {
expect(filter).to.be(undefined);
done();
});
$rootScope.$apply();
});
});

View file

@ -20,74 +20,57 @@
import sinon from 'sinon';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibGenerateMappingChainProvider } from '../generate_mapping_chain';
import { generateMappingChain } from '../generate_mapping_chain';
describe('Filter Bar Directive', function () {
describe('generateMappingChain()', function () {
let generateMappingChain;
let $rootScope;
let Promise;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, _$rootScope_, _Promise_) {
$rootScope = _$rootScope_;
Promise = _Promise_;
generateMappingChain = Private(FilterBarLibGenerateMappingChainProvider);
}));
it('should create a chaining function which calls the next function if the promise is rejected', function (done) {
const filter = {};
const mapping = sinon.stub();
mapping.returns(Promise.reject(filter));
const mappingChainFn = generateMappingChain(mapping);
mapping.rejects(filter);
const next = sinon.stub();
next.returns(Promise.resolve('good'));
const chain = mappingChainFn(next);
next.resolves('good');
const chain = generateMappingChain(mapping, next);
chain(filter).then(function (result) {
expect(result).to.be('good');
sinon.assert.calledOnce(next);
done();
});
$rootScope.$apply();
});
it('should create a chaining function which DOES NOT call the next function if the result is resolved', function (done) {
const mapping = sinon.stub();
mapping.returns(Promise.resolve('good'));
const mappingChainFn = generateMappingChain(mapping);
mapping.resolves('good');
const next = sinon.stub();
next.returns(Promise.resolve('bad'));
const chain = mappingChainFn(next);
next.resolves('bad');
const chain = generateMappingChain(mapping, next);
chain({}).then(function (result) {
expect(result).to.be('good');
sinon.assert.notCalled(next);
done();
});
$rootScope.$apply();
});
it('should resolve result for the mapping function', function (done) {
const mapping = sinon.stub();
mapping.returns(Promise.resolve({ key: 'test', value: 'example' }));
const mappingChainFn = generateMappingChain(mapping);
mapping.resolves({ key: 'test', value: 'example' });
const next = sinon.stub();
const chain = mappingChainFn(next);
const chain = generateMappingChain(mapping, next);
chain({}).then(function (result) {
sinon.assert.notCalled(next);
expect(result).to.eql({ key: 'test', value: 'example' });
done();
});
$rootScope.$apply();
});
it('should call the mapping function with the argument to the chain', function (done) {
const mapping = sinon.stub();
mapping.returns(Promise.resolve({ key: 'test', value: 'example' }));
const mappingChainFn = generateMappingChain(mapping);
mapping.resolves({ key: 'test', value: 'example' });
const next = sinon.stub();
const chain = mappingChainFn(next);
const chain = generateMappingChain(mapping, next);
chain({ test: 'example' }).then(function (result) {
sinon.assert.calledOnce(mapping);
expect(mapping.args[0][0]).to.eql({ test: 'example' });
@ -95,38 +78,33 @@ describe('Filter Bar Directive', function () {
expect(result).to.eql({ key: 'test', value: 'example' });
done();
});
$rootScope.$apply();
});
it('should resolve result for the next function', function (done) {
const filter = {};
const mapping = sinon.stub();
mapping.returns(Promise.reject(filter));
const mappingChainFn = generateMappingChain(mapping);
mapping.rejects(filter);
const next = sinon.stub();
next.returns(Promise.resolve({ key: 'test', value: 'example' }));
const chain = mappingChainFn(next);
next.resolves({ key: 'test', value: 'example' });
const chain = generateMappingChain(mapping, next);
chain(filter).then(function (result) {
sinon.assert.calledOnce(mapping);
sinon.assert.calledOnce(next);
expect(result).to.eql({ key: 'test', value: 'example' });
done();
});
$rootScope.$apply();
});
it('should reject with an error if no functions match', function (done) {
const filter = {};
const mapping = sinon.stub();
mapping.returns(Promise.reject(filter));
const mappingChainFn = generateMappingChain(mapping);
const chain = mappingChainFn();
mapping.rejects(filter);
const chain = generateMappingChain(mapping);
chain(filter).catch(function (err) {
expect(err).to.be.an(Error);
expect(err.message).to.be('No mappings have been found for filter.');
done();
});
$rootScope.$apply();
});
});

View file

@ -19,24 +19,20 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapAndFlattenFiltersProvider } from '../map_and_flatten_filters';
import { mapAndFlattenFilters } from '../map_and_flatten_filters';
import IndexPatternMock from 'fixtures/mock_index_patterns';
describe('Filter Bar Directive', function () {
describe('mapAndFlattenFilters()', function () {
let mapAndFlattenFilters;
let $rootScope;
let mockIndexPatterns;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
'kibana/courier'
));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
mapAndFlattenFilters = Private(FilterBarLibMapAndFlattenFiltersProvider);
$rootScope = _$rootScope_;
beforeEach(ngMock.inject(function (Private) {
mockIndexPatterns = Private(IndexPatternMock);
}));
const filters = [
@ -51,7 +47,7 @@ describe('Filter Bar Directive', function () {
];
it('should map and flatten the filters', function (done) {
mapAndFlattenFilters(filters).then(function (results) {
mapAndFlattenFilters(mockIndexPatterns, filters).then(function (results) {
expect(results).to.have.length(5);
expect(results[0]).to.have.property('meta');
expect(results[1]).to.have.property('meta');
@ -70,7 +66,6 @@ describe('Filter Bar Directive', function () {
expect(results[4].meta).to.have.property('value', 'apache');
done();
});
$rootScope.$apply();
});
});

View file

@ -18,20 +18,11 @@
*/
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapDefaultProvider } from '../map_default';
import { mapDefault } from '../map_default';
describe('Filter Bar Directive', function () {
describe('mapDefault()', function () {
let mapDefault;
let $rootScope;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapDefault = Private(FilterBarLibMapDefaultProvider);
}));
it('should return the key and value for matching filters', function (done) {
const filter = { query: { match_all: {} } };
mapDefault(filter).then(function (result) {
@ -39,7 +30,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.have.property('value', '{"match_all":{}}');
done();
});
$rootScope.$apply();
});
it('should work with undefined filter types', function (done) {
@ -57,7 +47,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.have.property('value', JSON.stringify(filter.bool));
done();
});
$rootScope.$apply();
});
it('should return undefined if there is no valid key', function (done) {
@ -66,7 +55,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});

View file

@ -19,18 +19,12 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapExistsProvider } from '../map_exists';
import { mapExists } from '../map_exists';
describe('Filter Bar Directive', function () {
describe('mapExists()', function () {
let mapExists;
let $rootScope;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapExists = Private(FilterBarLibMapExistsProvider);
}));
it('should return the key and value for matching filters', function (done) {
const filter = { exists: { field: '_type' } };
@ -39,7 +33,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.have.property('value', 'exists');
done();
});
$rootScope.$apply();
});
it('should return undefined for none matching', function (done) {
@ -48,7 +41,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
});

View file

@ -19,30 +19,25 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapFilterProvider } from '../map_filter';
import { mapFilter } from '../map_filter';
import IndexPatternMock from 'fixtures/mock_index_patterns';
describe('Filter Bar Directive', function () {
let mapFilter;
let $rootScope;
let mockIndexPatterns;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
'kibana/courier'
));
beforeEach(ngMock.inject(function (_$rootScope_, Private) {
mapFilter = Private(FilterBarLibMapFilterProvider);
$rootScope = _$rootScope_;
beforeEach(ngMock.inject(function (Private) {
mockIndexPatterns = Private(IndexPatternMock);
}));
describe('mapFilter()', function () {
it('should map query filters', function (done) {
const before = { meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'apache' } } } };
mapFilter(before).then(function (after) {
mapFilter(mockIndexPatterns, before).then(function (after) {
expect(after).to.have.property('meta');
expect(after.meta).to.have.property('key', '_type');
expect(after.meta).to.have.property('value', 'apache');
@ -50,12 +45,11 @@ describe('Filter Bar Directive', function () {
expect(after.meta).to.have.property('negate', false);
done();
});
$rootScope.$apply();
});
it('should map exists filters', function (done) {
const before = { meta: { index: 'logstash-*' }, exists: { field: '@timestamp' } };
mapFilter(before).then(function (after) {
mapFilter(mockIndexPatterns, before).then(function (after) {
expect(after).to.have.property('meta');
expect(after.meta).to.have.property('key', '@timestamp');
expect(after.meta).to.have.property('value', 'exists');
@ -63,12 +57,11 @@ describe('Filter Bar Directive', function () {
expect(after.meta).to.have.property('negate', false);
done();
});
$rootScope.$apply();
});
it('should map missing filters', function (done) {
const before = { meta: { index: 'logstash-*' }, missing: { field: '@timestamp' } };
mapFilter(before).then(function (after) {
mapFilter(mockIndexPatterns, before).then(function (after) {
expect(after).to.have.property('meta');
expect(after.meta).to.have.property('key', '@timestamp');
expect(after.meta).to.have.property('value', 'missing');
@ -76,12 +69,11 @@ describe('Filter Bar Directive', function () {
expect(after.meta).to.have.property('negate', false);
done();
});
$rootScope.$apply();
});
it('should map json filter', function (done) {
const before = { meta: { index: 'logstash-*' }, query: { match_all: {} } };
mapFilter(before).then(function (after) {
mapFilter(mockIndexPatterns, before).then(function (after) {
expect(after).to.have.property('meta');
expect(after.meta).to.have.property('key', 'query');
expect(after.meta).to.have.property('value', '{"match_all":{}}');
@ -89,17 +81,15 @@ describe('Filter Bar Directive', function () {
expect(after.meta).to.have.property('negate', false);
done();
});
$rootScope.$apply();
});
it('should finish with a catch', function (done) {
const before = { meta: { index: 'logstash-*' } };
mapFilter(before).catch(function (error) {
mapFilter(mockIndexPatterns, before).catch(function (error) {
expect(error).to.be.an(Error);
expect(error.message).to.be('No mappings have been found for filter.');
done();
});
$rootScope.$apply();
});
});

View file

@ -1,68 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapFlattenAndWrapFiltersProvider } from '../map_flatten_and_wrap_filters';
describe('Filter Bar Directive', function () {
describe('mapFlattenAndWrapFilters()', function () {
let mapFlattenAndWrapFilters;
let $rootScope;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
mapFlattenAndWrapFilters = Private(FilterBarLibMapFlattenAndWrapFiltersProvider);
$rootScope = _$rootScope_;
}));
const filters = [
null,
[
{ meta: { index: 'logstash-*' }, exists: { field: '_type' } },
{ meta: { index: 'logstash-*' }, missing: { field: '_type' } }
],
{ meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } } },
{ meta: { index: 'logstash-*' }, range: { bytes: { lt: 2048, gt: 1024 } } },
{ meta: { index: 'logstash-*' }, query: { match: { _type: { query: 'apache', type: 'phrase' } } } }
];
it('should map, flatten and wrap filters', function (done) {
mapFlattenAndWrapFilters(filters).then(function (results) {
expect(results).to.have.length(5);
_.each(results, function (filter) {
expect(filter).to.have.property('meta');
expect(filter.meta).to.have.property('apply', true);
});
done();
});
$rootScope.$apply();
});
});
});

View file

@ -19,24 +19,22 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapGeoBoundingBoxProvider } from '../map_geo_bounding_box';
import { mapGeoBoundingBox } from '../map_geo_bounding_box';
import IndexPatternMock from 'fixtures/mock_index_patterns';
describe('Filter Bar Directive', function () {
describe('mapGeoBoundingBox()', function () {
let mapGeoBoundingBox;
let $rootScope;
let mapGeoBoundingBoxFn;
let mockIndexPatterns;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
'kibana/courier'
));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
mapGeoBoundingBox = Private(FilterBarLibMapGeoBoundingBoxProvider);
$rootScope = _$rootScope_;
beforeEach(ngMock.inject(function (Private) {
mockIndexPatterns = Private(IndexPatternMock);
mapGeoBoundingBoxFn = mapGeoBoundingBox(mockIndexPatterns);
}));
it('should return the key and value for matching filters with bounds', function (done) {
@ -51,23 +49,21 @@ describe('Filter Bar Directive', function () {
}
}
};
mapGeoBoundingBox(filter).then(function (result) {
mapGeoBoundingBoxFn(filter).then(function (result) {
expect(result).to.have.property('key', 'point');
expect(result).to.have.property('value');
// remove html entities and non-alphanumerics to get the gist of the value
expect(result.value.replace(/&[a-z]+?;/g, '').replace(/[^a-z0-9]/g, '')).to.be('lat5lon10tolat15lon20');
done();
});
$rootScope.$apply();
});
it('should return undefined for none matching', function (done) {
const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } } };
mapGeoBoundingBox(filter).catch(function (result) {
mapGeoBoundingBoxFn(filter).catch(function (result) {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
it('should return the key and value even when using ignore_unmapped', function (done) {
@ -83,14 +79,13 @@ describe('Filter Bar Directive', function () {
}
}
};
mapGeoBoundingBox(filter).then(function (result) {
mapGeoBoundingBoxFn(filter).then(function (result) {
expect(result).to.have.property('key', 'point');
expect(result).to.have.property('value');
// remove html entities and non-alphanumerics to get the gist of the value
expect(result.value.replace(/&[a-z]+?;/g, '').replace(/[^a-z0-9]/g, '')).to.be('lat5lon10tolat15lon20');
done();
});
$rootScope.$apply();
});
});

View file

@ -19,24 +19,22 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapGeoPolygonProvider } from '../map_geo_polygon';
import { mapGeoPolygon } from '../map_geo_polygon';
import IndexPatternMock from 'fixtures/mock_index_patterns';
describe('Filter Bar Directive', function () {
describe('mapGeoPolygon()', function () {
let mapGeoPolygon;
let $rootScope;
let mapGeoPolygonFn;
let mockIndexPatterns;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
'kibana/courier'
));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
mapGeoPolygon = Private(FilterBarLibMapGeoPolygonProvider);
$rootScope = _$rootScope_;
beforeEach(ngMock.inject(function (Private) {
mockIndexPatterns = Private(IndexPatternMock);
mapGeoPolygonFn = mapGeoPolygon(mockIndexPatterns);
}));
it('should return the key and value for matching filters with bounds', function (done) {
@ -53,23 +51,21 @@ describe('Filter Bar Directive', function () {
}
}
};
mapGeoPolygon(filter).then(function (result) {
mapGeoPolygonFn(filter).then(function (result) {
expect(result).to.have.property('key', 'point');
expect(result).to.have.property('value');
// remove html entities and non-alphanumerics to get the gist of the value
expect(result.value.replace(/&[a-z]+?;/g, '').replace(/[^a-z0-9]/g, '')).to.be('lat5lon10lat15lon20');
done();
});
$rootScope.$apply();
});
it('should return undefined for none matching', function (done) {
const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } } };
mapGeoPolygon(filter).catch(function (result) {
mapGeoPolygonFn(filter).catch(function (result) {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
it('should return the key and value even when using ignore_unmapped', function (done) {
@ -87,14 +83,13 @@ describe('Filter Bar Directive', function () {
}
}
};
mapGeoPolygon(filter).then(function (result) {
mapGeoPolygonFn(filter).then(function (result) {
expect(result).to.have.property('key', 'point');
expect(result).to.have.property('value');
// remove html entities and non-alphanumerics to get the gist of the value
expect(result.value.replace(/&[a-z]+?;/g, '').replace(/[^a-z0-9]/g, '')).to.be('lat5lon10lat15lon20');
done();
});
$rootScope.$apply();
});
});

View file

@ -19,19 +19,15 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapMatchAllProvider } from '../map_match_all';
import { mapMatchAll } from '../map_match_all';
describe('ui/filter_bar/lib', function () {
describe('mapMatchAll()', function () {
let $rootScope;
let mapMatchAll;
let filter;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapMatchAll = Private(FilterBarLibMapMatchAllProvider);
beforeEach(ngMock.inject(function () {
filter = {
match_all: {},
meta: {
@ -48,15 +44,16 @@ describe('ui/filter_bar/lib', function () {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
});
describe('when given a match_all filter', function () {
let result;
beforeEach(function () {
mapMatchAll(filter).then(r => result = r);
$rootScope.$apply();
beforeEach(function (done) {
mapMatchAll(filter).then(r => {
result = r;
done();
});
});
it('key is set to meta field', function () {

View file

@ -17,22 +17,12 @@
* under the License.
*/
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import { FilterBarLibMapMissingProvider } from '../map_missing';
import { mapMissing } from '../map_missing';
describe('Filter Bar Directive', function () {
describe('mapMissing()', function () {
let mapMissing;
let $rootScope;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapMissing = Private(FilterBarLibMapMissingProvider);
}));
it('should return the key and value for matching filters', function (done) {
const filter = { missing: { field: '_type' } };
mapMissing(filter).then(function (result) {
@ -40,7 +30,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.have.property('value', 'missing');
done();
});
$rootScope.$apply();
});
it('should return undefined for none matching', function (done) {
@ -49,7 +38,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
});

View file

@ -19,43 +19,39 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapPhraseProvider } from '../map_phrase';
import { mapPhrase } from '../map_phrase';
import IndexPatternMock from 'fixtures/mock_index_patterns';
describe('Filter Bar Directive', function () {
describe('mapPhrase()', function () {
let mapPhrase;
let $rootScope;
let mapPhraseFn;
let mockIndexPatterns;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
'kibana/courier'
));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapPhrase = Private(FilterBarLibMapPhraseProvider);
beforeEach(ngMock.inject(function (Private) {
mockIndexPatterns = Private(IndexPatternMock);
mapPhraseFn = mapPhrase(mockIndexPatterns);
}));
it('should return the key and value for matching filters', function (done) {
const filter = { meta: { index: 'logstash-*' }, query: { match: { _type: { query: 'apache', type: 'phrase' } } } };
mapPhrase(filter).then(function (result) {
mapPhraseFn(filter).then(function (result) {
expect(result).to.have.property('key', '_type');
expect(result).to.have.property('value', 'apache');
done();
});
$rootScope.$apply();
});
it('should return undefined for none matching', function (done) {
const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } } };
mapPhrase(filter).catch(function (result) {
mapPhraseFn(filter).catch(function (result) {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
});

View file

@ -18,18 +18,10 @@
*/
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapQueryStringProvider } from '../map_query_string';
import { mapQueryString } from '../map_query_string';
describe('Filter Bar Directive', function () {
describe('mapQueryString()', function () {
let mapQueryString;
let $rootScope;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapQueryString = Private(FilterBarLibMapQueryStringProvider);
}));
it('should return the key and value for matching filters', function (done) {
const filter = { query: { query_string: { query: 'foo:bar' } } };
@ -38,7 +30,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.have.property('value', 'foo:bar');
done();
});
$rootScope.$apply();
});
it('should return undefined for none matching', function (done) {
@ -47,7 +38,6 @@ describe('Filter Bar Directive', function () {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
});

View file

@ -19,53 +19,48 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { FilterBarLibMapRangeProvider } from '../map_range';
import { mapRange } from '../map_range';
import IndexPatternMock from 'fixtures/mock_index_patterns';
describe('Filter Bar Directive', function () {
describe('mapRange()', function () {
let mapRange;
let $rootScope;
let mapRangeFn;
let mockIndexPatterns;
beforeEach(ngMock.module(
'kibana',
'kibana/courier',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
'kibana/courier'
));
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
mapRange = Private(FilterBarLibMapRangeProvider);
$rootScope = _$rootScope_;
beforeEach(ngMock.inject(function (Private) {
mockIndexPatterns = Private(IndexPatternMock);
mapRangeFn = mapRange(mockIndexPatterns);
}));
it('should return the key and value for matching filters with gt/lt', function (done) {
const filter = { meta: { index: 'logstash-*' }, range: { bytes: { lt: 2048, gt: 1024 } } };
mapRange(filter).then(function (result) {
mapRangeFn(filter).then(function (result) {
expect(result).to.have.property('key', 'bytes');
expect(result).to.have.property('value', '1,024 to 2,048');
done();
});
$rootScope.$apply();
});
it('should return the key and value for matching filters with gte/lte', function (done) {
const filter = { meta: { index: 'logstash-*' }, range: { bytes: { lte: 2048, gte: 1024 } } };
mapRange(filter).then(function (result) {
mapRangeFn(filter).then(function (result) {
expect(result).to.have.property('key', 'bytes');
expect(result).to.have.property('value', '1,024 to 2,048');
done();
});
$rootScope.$apply();
});
it('should return undefined for none matching', function (done) {
const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } } };
mapRange(filter).catch(function (result) {
mapRangeFn(filter).catch(function (result) {
expect(result).to.be(filter);
done();
});
$rootScope.$apply();
});
});

View file

@ -19,21 +19,19 @@
import _ from 'lodash';
export function FilterBarLibExtractTimeFilterProvider(indexPatterns, Promise) {
return Promise.method(function (filters) {
// Assume all the index patterns are the same since they will be added
// from the same visualization.
const id = _.get(filters, '[0].meta.index');
if (id == null) return;
export async function extractTimeFilter(indexPatterns, filters) {
// Assume all the index patterns are the same since they will be added
// from the same visualization.
const id = _.get(filters, '[0].meta.index');
if (id == null) return;
return indexPatterns.get(id).then(function (indexPattern) {
const filter = _.find(filters, function (obj) {
const key = _.keys(obj.range)[0];
return key === indexPattern.timeFieldName;
});
if (filter && filter.range) {
return filter;
}
});
const indexPattern = await indexPatterns.get(id);
const filter = _.find(filters, function (obj) {
const key = _.keys(obj.range)[0];
return key === indexPattern.timeFieldName;
});
if (filter && filter.range) {
return filter;
}
}

View file

@ -17,24 +17,18 @@
* under the License.
*/
export function FilterBarLibGenerateMappingChainProvider(Promise) {
export function generateMappingChain(fn, next) {
const noop = function () {
return Promise.reject(new Error('No mappings have been found for filter.'));
throw new Error('No mappings have been found for filter.');
};
return function (fn) {
return function (next) {
next = next || noop;
return function (filter) {
return fn(filter).catch(function (result) {
if (result === filter) {
return next(filter);
}
return Promise.reject(result);
});
};
};
next = next || noop;
return async function (filter) {
return await fn(filter).catch(function (result) {
if (result === filter) {
return next(filter);
}
throw result;
});
};
}

View file

@ -18,16 +18,12 @@
*/
import _ from 'lodash';
import { FilterBarLibMapFilterProvider } from './map_filter';
import { mapFilter } from './map_filter';
export function FilterBarLibMapAndFlattenFiltersProvider(Private, Promise) {
const mapFilter = Private(FilterBarLibMapFilterProvider);
return function (filters) {
return _(filters)
.flatten()
.compact()
.map(mapFilter)
.thru(Promise.all)
.value();
};
export function mapAndFlattenFilters(indexPatterns, filters) {
const flattened = _(filters)
.flatten()
.compact()
.map(item => mapFilter(indexPatterns, item)).value();
return Promise.all(flattened);
}

View file

@ -20,21 +20,18 @@
import angular from 'angular';
import _ from 'lodash';
export function FilterBarLibMapDefaultProvider(Promise) {
export async function mapDefault(filter) {
const metaProperty = /(^\$|meta)/;
return function (filter) {
const key = _.find(_.keys(filter), function (key) {
return !key.match(metaProperty);
});
const key = _.find(_.keys(filter), function (key) {
return !key.match(metaProperty);
});
if (key) {
const type = 'custom';
const value = angular.toJson(filter[key]);
return Promise.resolve({ type, key, value });
}
if (key) {
const type = 'custom';
const value = angular.toJson(filter[key]);
return { type, key, value };
}
return Promise.reject(filter);
};
throw filter;
}

View file

@ -17,14 +17,12 @@
* under the License.
*/
export function FilterBarLibMapExistsProvider(Promise) {
return function (filter) {
if (filter.exists) {
const type = 'exists';
const key = filter.exists.field;
const value = type;
return Promise.resolve({ type, key, value });
}
return Promise.reject(filter);
};
export async function mapExists(filter) {
if (filter.exists) {
const type = 'exists';
const key = filter.exists.field;
const value = type;
return { type, key, value };
}
throw filter;
}

View file

@ -18,22 +18,19 @@
*/
import _ from 'lodash';
import { FilterBarLibGenerateMappingChainProvider } from './generate_mapping_chain';
import { FilterBarLibMapMatchAllProvider } from './map_match_all';
import { FilterBarLibMapPhraseProvider } from './map_phrase';
import { FilterBarLibMapPhrasesProvider } from './map_phrases';
import { FilterBarLibMapRangeProvider } from './map_range';
import { FilterBarLibMapExistsProvider } from './map_exists';
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 { FilterBarLibMapDefaultProvider } from './map_default';
export function FilterBarLibMapFilterProvider(Promise, Private) {
const generateMappingChain = Private(FilterBarLibGenerateMappingChainProvider);
import { mapMatchAll } from './map_match_all';
import { mapPhrase } from './map_phrase';
import { mapPhrases } from './map_phrases';
import { mapRange } from './map_range';
import { mapExists } from './map_exists';
import { mapMissing } from './map_missing';
import { mapQueryString } from './map_query_string';
import { mapGeoBoundingBox } from './map_geo_bounding_box';
import { mapGeoPolygon } from './map_geo_polygon';
import { mapDefault } from './map_default';
import { generateMappingChain } from './generate_mapping_chain';
export async function mapFilter(indexPatterns, filter) {
/** Mappers **/
// Each mapper is a simple promise function that test if the mapper can
@ -51,47 +48,40 @@ export function FilterBarLibMapFilterProvider(Promise, Private) {
// that either handles the mapping operation or not
// and add it here. ProTip: These are executed in order listed
const mappers = [
Private(FilterBarLibMapMatchAllProvider),
Private(FilterBarLibMapRangeProvider),
Private(FilterBarLibMapPhraseProvider),
Private(FilterBarLibMapPhrasesProvider),
Private(FilterBarLibMapExistsProvider),
Private(FilterBarLibMapMissingProvider),
Private(FilterBarLibMapQueryStringProvider),
Private(FilterBarLibMapGeoBoundingBoxProvider),
Private(FilterBarLibMapGeoPolygonProvider),
Private(FilterBarLibMapDefaultProvider),
mapMatchAll,
mapRange(indexPatterns),
mapPhrase(indexPatterns),
mapPhrases,
mapExists,
mapMissing,
mapQueryString,
mapGeoBoundingBox(indexPatterns),
mapGeoPolygon(indexPatterns),
mapDefault,
];
const noop = function () {
return Promise.reject(new Error('No mappings have been found for filter.'));
throw new Error('No mappings have been found for filter.');
};
// Create a chain of responsibility by reducing all the
// mappers down into one function.
const mapFn = _.reduceRight(mappers, function (memo, map) {
const filterChainFn = generateMappingChain(map);
return filterChainFn(memo);
return generateMappingChain(map, memo);
}, noop);
/**
* Map the filter into an object with the key and value exposed so it's
* easier to work with in the template
* @param {object} filter The filter the map
* @returns {Promise}
*/
return function (filter) {
// Apply the mapping function
return mapFn(filter).then(function (result) {
filter.meta = filter.meta || {};
filter.meta.type = result.type;
filter.meta.key = result.key;
filter.meta.value = result.value;
filter.meta.params = result.params;
filter.meta.disabled = !!(filter.meta.disabled);
filter.meta.negate = !!(filter.meta.negate);
filter.meta.alias = filter.meta.alias || null;
return filter;
});
};
const mapped = await mapFn(filter);
// Map the filter into an object with the key and value exposed so it's
// easier to work with in the template
filter.meta = filter.meta || {};
filter.meta.type = mapped.type;
filter.meta.key = mapped.key;
filter.meta.value = mapped.value;
filter.meta.params = mapped.params;
filter.meta.disabled = !!(filter.meta.disabled);
filter.meta.negate = !!(filter.meta.negate);
filter.meta.alias = filter.meta.alias || null;
return filter;
}

View file

@ -1,34 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import { FilterBarLibMapAndFlattenFiltersProvider } from './map_and_flatten_filters';
export function FilterBarLibMapFlattenAndWrapFiltersProvider(Private) {
const mapAndFlattenFilters = Private(FilterBarLibMapAndFlattenFiltersProvider);
return function (filters) {
return mapAndFlattenFilters(filters).then(function (filters) {
return _.map(filters, function (filter) {
filter.meta = filter.meta || {};
filter.meta.apply = true;
return filter;
});
});
};
}

View file

@ -20,39 +20,39 @@
import _ from 'lodash';
import { SavedObjectNotFound } from '../../errors';
export function FilterBarLibMapGeoBoundingBoxProvider(Promise, indexPatterns) {
return function (filter) {
if (filter.geo_bounding_box) {
function getParams(indexPattern) {
const type = 'geo_bounding_box';
const key = _.keys(filter.geo_bounding_box)
.filter(key => key !== 'ignore_unmapped')[0];
const params = filter.geo_bounding_box[key];
function getParams(filter, indexPattern) {
const type = 'geo_bounding_box';
const key = _.keys(filter.geo_bounding_box)
.filter(key => key !== 'ignore_unmapped')[0];
const params = filter.geo_bounding_box[key];
// 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 };
}
// 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 indexPatterns
.get(filter.meta.index)
.then(getParams)
.catch((error) => {
if (error instanceof SavedObjectNotFound) {
return getParams();
}
throw error;
});
export function mapGeoBoundingBox(indexPatterns) {
return async function (filter) {
if (!filter.geo_bounding_box) {
throw filter;
}
try {
const indexPattern = await indexPatterns.get(filter.meta.index);
return getParams(filter, indexPattern);
} catch (error) {
if (error instanceof SavedObjectNotFound) {
return getParams(filter);
}
throw error;
}
return Promise.reject(filter);
};
}

View file

@ -20,38 +20,39 @@
import _ from 'lodash';
import { SavedObjectNotFound } from '../../errors';
export function FilterBarLibMapGeoPolygonProvider(Promise, indexPatterns) {
return function (filter) {
if (filter.geo_polygon) {
function getParams(indexPattern) {
const type = 'geo_polygon';
const key = _.keys(filter.geo_polygon)
.filter(key => key !== 'ignore_unmapped')[0];
const params = filter.geo_polygon[key];
function getParams(filter, indexPattern) {
const type = 'geo_polygon';
const key = _.keys(filter.geo_polygon)
.filter(key => key !== 'ignore_unmapped')[0];
const params = filter.geo_polygon[key];
// 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 };
}
// 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 indexPatterns
.get(filter.meta.index)
.then(getParams)
.catch((error) => {
if (error instanceof SavedObjectNotFound) {
return getParams();
}
throw error;
});
export function mapGeoPolygon(indexPatterns) {
return async function (filter) {
if (!filter.geo_polygon) {
throw filter;
}
try {
const indexPattern = await indexPatterns.get(filter.meta.index);
return getParams(filter, indexPattern);
} catch (error) {
if (error instanceof SavedObjectNotFound) {
return getParams(filter);
}
throw error;
}
return Promise.reject(filter);
};
}

View file

@ -17,14 +17,12 @@
* under the License.
*/
export function FilterBarLibMapMatchAllProvider(Promise) {
return function (filter) {
if (filter.match_all) {
const type = 'match_all';
const key = filter.meta.field;
const value = filter.meta.formattedValue || 'all';
return Promise.resolve({ type, key, value });
}
return Promise.reject(filter);
};
export async function mapMatchAll(filter) {
if (filter.match_all) {
const type = 'match_all';
const key = filter.meta.field;
const value = filter.meta.formattedValue || 'all';
return { type, key, value };
}
throw filter;
}

View file

@ -17,14 +17,12 @@
* under the License.
*/
export function FilterBarLibMapMissingProvider(Promise) {
return function (filter) {
if (filter.missing) {
const type = 'missing';
const key = filter.missing.field;
const value = type;
return Promise.resolve({ type, key, value });
}
return Promise.reject(filter);
};
export async function mapMissing(filter) {
if (filter.missing) {
const type = 'missing';
const key = filter.missing.field;
const value = type;
return { type, key, value };
}
throw filter;
}

View file

@ -20,40 +20,41 @@
import _ from 'lodash';
import { SavedObjectNotFound } from '../../errors';
export function FilterBarLibMapPhraseProvider(Promise, indexPatterns) {
return function (filter) {
const isScriptedPhraseFilter = isScriptedPhrase(filter);
if (!_.has(filter, ['query', 'match']) && !isScriptedPhraseFilter) {
return Promise.reject(filter);
}
function getParams(indexPattern) {
const type = 'phrase';
const key = isScriptedPhraseFilter ? filter.meta.field : Object.keys(filter.query.match)[0];
const query = isScriptedPhraseFilter ? filter.script.script.params.value : filter.query.match[key].query;
const params = { query };
// Sometimes a filter will end up with an invalid index or field 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 or field is invalid.
const value = (indexPattern && indexPattern.fields.byName[key]) ? indexPattern.fields.byName[key].format.convert(query) : query;
return { type, key, value, params };
}
return indexPatterns
.get(filter.meta.index)
.then(getParams)
.catch((error) => {
if (error instanceof SavedObjectNotFound) {
return getParams();
}
throw error;
});
};
}
function isScriptedPhrase(filter) {
const value = _.get(filter, ['script', 'script', 'params', 'value']);
return typeof value !== 'undefined';
}
function getParams(filter, indexPattern) {
const isScriptedPhraseFilter = isScriptedPhrase(filter);
const type = 'phrase';
const key = isScriptedPhraseFilter ? filter.meta.field : Object.keys(filter.query.match)[0];
const query = isScriptedPhraseFilter ? filter.script.script.params.value : filter.query.match[key].query;
const params = { query };
// Sometimes a filter will end up with an invalid index or field 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 or field is invalid.
const value = (indexPattern && indexPattern.fields.byName[key]) ? indexPattern.fields.byName[key].format.convert(query) : query;
return { type, key, value, params };
}
export function mapPhrase(indexPatterns) {
return async function (filter) {
const isScriptedPhraseFilter = isScriptedPhrase(filter);
if (!_.has(filter, ['query', 'match']) && !isScriptedPhraseFilter) {
throw filter;
}
try {
const indexPattern = await indexPatterns.get(filter.meta.index);
return getParams(filter, indexPattern);
} catch (error) {
if (error instanceof SavedObjectNotFound) {
return getParams(filter);
}
throw error;
}
};
}

View file

@ -17,13 +17,11 @@
* under the License.
*/
export function FilterBarLibMapPhrasesProvider(Promise) {
return function (filter) {
const { type, key, value, params } = filter.meta;
if (type !== 'phrases') {
return Promise.reject(filter);
} else {
return Promise.resolve({ type, key, value, params });
}
};
export async function mapPhrases(filter) {
const { type, key, value, params } = filter.meta;
if (type !== 'phrases') {
throw filter;
} else {
return { type, key, value, params };
}
}

View file

@ -17,14 +17,12 @@
* under the License.
*/
export function FilterBarLibMapQueryStringProvider(Promise) {
return function (filter) {
if (filter.query && filter.query.query_string) {
const type = 'query_string';
const key = 'query';
const value = filter.query.query_string.query;
return Promise.resolve({ type, key, value });
}
return Promise.reject(filter);
};
export async function mapQueryString(filter) {
if (filter.query && filter.query.query_string) {
const type = 'query_string';
const key = 'query';
const value = filter.query.query_string.query;
return { type, key, value };
}
throw filter;
}

View file

@ -20,51 +20,52 @@
import { has, get } from 'lodash';
import { SavedObjectNotFound } from '../../errors';
export function FilterBarLibMapRangeProvider(Promise, indexPatterns) {
return function (filter) {
const isScriptedRangeFilter = isScriptedRange(filter);
if (!filter.range && !isScriptedRangeFilter) {
return Promise.reject(filter);
}
function getParams(indexPattern) {
const type = 'range';
const key = isScriptedRangeFilter ? filter.meta.field : Object.keys(filter.range)[0];
const params = isScriptedRangeFilter ? filter.script.script.params : filter.range[key];
let left = has(params, 'gte') ? params.gte : params.gt;
if (left == null) left = -Infinity;
let right = has(params, 'lte') ? params.lte : params.lt;
if (right == null) right = Infinity;
// 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 indexPatterns
.get(filter.meta.index)
.then(getParams)
.catch((error) => {
if (error instanceof SavedObjectNotFound) {
return getParams();
}
throw error;
});
};
}
function isScriptedRange(filter) {
const params = get(filter, ['script', 'script', 'params']);
return params && Object.keys(params).find(key => ['gte', 'gt', 'lte', 'lt'].includes(key));
}
function getParams(filter, indexPattern) {
const isScriptedRangeFilter = isScriptedRange(filter);
const type = 'range';
const key = isScriptedRangeFilter ? filter.meta.field : Object.keys(filter.range)[0];
const params = isScriptedRangeFilter ? filter.script.script.params : filter.range[key];
let left = has(params, 'gte') ? params.gte : params.gt;
if (left == null) left = -Infinity;
let right = has(params, 'lte') ? params.lte : params.lt;
if (right == null) right = Infinity;
// 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 };
}
export function mapRange(indexPatterns) {
return async function (filter) {
const isScriptedRangeFilter = isScriptedRange(filter);
if (!filter.range && !isScriptedRangeFilter) {
throw filter;
}
try {
const indexPattern = await indexPatterns.get(filter.meta.index);
return getParams(filter, indexPattern);
} catch (error) {
if (error instanceof SavedObjectNotFound) {
return getParams(filter);
}
throw error;
}
};
}

View file

@ -1,34 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
export function FilterBarPushFilterProvider() {
return function ($state) {
if (!_.isObject($state)) throw new Error('pushFilters requires a state object');
return function (filter, negate, index) {
// Hierarchical and tabular data set their aggConfigResult parameter
// differently because of how the point is rewritten between the two. So
// we need to check if the point.orig is set, if not use try the point.aggConfigResult
const pendingFilter = { meta: { negate: negate, index: index } };
_.extend(pendingFilter, filter);
$state.$newFilters = [pendingFilter];
};
};
}

View file

@ -19,11 +19,9 @@
import _ from 'lodash';
export function FilterBarPushFiltersProvider() {
return function ($state) {
if (!_.isObject($state)) throw new Error('pushFilters requires a state object');
return function (filters) {
$state.$newFilters = filters;
};
};
// TODO: should it be here or in vis filters (only place where it's used).
// $newFilters is not defined by filter_bar as well.
export function pushFilterBarFilters($state, filters) {
if (!_.isObject($state)) throw new Error('pushFilters requires a state object');
$state.$newFilters = filters;
}

View file

@ -23,14 +23,12 @@ import { onlyStateChanged } from './lib/only_state_changed';
import { uniqFilters } from './lib/uniq_filters';
import { compareFilters } from './lib/compare_filters';
import { EventsProvider } from '../events';
import { FilterBarLibMapAndFlattenFiltersProvider } from './lib/map_and_flatten_filters';
import { FilterBarLibExtractTimeFilterProvider } from './lib/extract_time_filter';
import { mapAndFlattenFilters } from './lib/map_and_flatten_filters';
import { extractTimeFilter } from './lib/extract_time_filter';
import { changeTimeFilter } from './lib/change_time_filter';
export function FilterBarQueryFilterProvider(Private, $rootScope, getAppState, globalState, config) {
export function FilterBarQueryFilterProvider(Private, Promise, indexPatterns, $rootScope, getAppState, globalState, config) {
const EventEmitter = Private(EventsProvider);
const mapAndFlattenFilters = Private(FilterBarLibMapAndFlattenFiltersProvider);
const extractTimeFilter = Private(FilterBarLibExtractTimeFilterProvider);
const queryFilter = new EventEmitter();
@ -85,7 +83,7 @@ export function FilterBarQueryFilterProvider(Private, $rootScope, getAppState, g
filters = [filters];
}
return mapAndFlattenFilters(filters)
return Promise.resolve(mapAndFlattenFilters(indexPatterns, filters))
.then(function (filters) {
if (!filterState.filters) {
filterState.filters = [];
@ -220,7 +218,7 @@ export function FilterBarQueryFilterProvider(Private, $rootScope, getAppState, g
};
queryFilter.setFilters = filters => {
return mapAndFlattenFilters(filters)
return Promise.resolve(mapAndFlattenFilters(indexPatterns, filters))
.then(mappedFilters => {
const appState = getAppState();
const [globalFilters, appFilters] = _.partition(mappedFilters, filter => {
@ -232,7 +230,7 @@ export function FilterBarQueryFilterProvider(Private, $rootScope, getAppState, g
};
queryFilter.addFiltersAndChangeTimeFilter = async filters => {
const timeFilter = await extractTimeFilter(filters);
const timeFilter = await extractTimeFilter(indexPatterns, filters);
if (timeFilter) changeTimeFilter(timeFilter);
queryFilter.addFilters(filters.filter(filter => filter !== timeFilter));
};

View file

@ -18,7 +18,7 @@
*/
import _ from 'lodash';
import { FilterBarPushFiltersProvider } from '../filter_bar/push_filters';
import { pushFilterBarFilters } from '../filter_bar/push_filters';
import { FilterBarQueryFilterProvider } from '../filter_bar/query_filter';
import { onBrushEvent } from '../utils/brush_event';
@ -84,7 +84,6 @@ const createFilter = (aggConfigs, table, columnIndex, rowIndex, cellValue) => {
};
const VisFiltersProvider = (Private, getAppState) => {
const filterBarPushFilters = Private(FilterBarPushFiltersProvider);
const queryFilter = Private(FilterBarQueryFilterProvider);
const pushFilters = (filters, simulate) => {
@ -92,7 +91,7 @@ const VisFiltersProvider = (Private, getAppState) => {
if (filters.length && !simulate) {
const flatFilters = _.flatten(filters);
const deduplicatedFilters = flatFilters.filter((v, i) => i === flatFilters.findIndex(f => _.isEqual(v, f)));
filterBarPushFilters(appState)(deduplicatedFilters);
pushFilterBarFilters(appState, deduplicatedFilters);
}
};