Skip to content
This repository has been archived by the owner on Mar 31, 2024. It is now read-only.

Commit

Permalink
[IndexPatterns] Support cross cluster search (elastic#11114)
Browse files Browse the repository at this point in the history
* [indexPatterns] support cross cluster patterns

* [vis] remove unused `hasTimeField` param

* [indexPatterns/create] fix method name in view

* [indexPatterns/create] disallow expanding with ccs

* [indexPatterns/create] field fetching is cheaper, react faster

* [indexPatterns/resolveTimePattern/tests] increase readability

* [tests/apiIntegration/indexPatterns] test conflict field output

* [indexPatterns/fieldCaps/readFieldCapsResponse] add unit tests

* [test/apiIntegration] ensure random word will not be valid

* [indexPatterns/ui/client] remove unused import

* remove use of auto-release-sinon

* [indexPatterns/create] don't allow expand when cross cluster

* [indexPatternsApiClient/stub] use angular promises

* [indexPatterns/create] add tests for base create ui behaviors

(cherry picked from commit e5deca6)
  • Loading branch information
spalger committed Jun 2, 2017
1 parent 79ca78a commit 18f63d2
Show file tree
Hide file tree
Showing 98 changed files with 2,692 additions and 938 deletions.
2 changes: 0 additions & 2 deletions src/core_plugins/kibana/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Promise from 'bluebird';
import { mkdirp as mkdirpNode } from 'mkdirp';

import manageUuid from './server/lib/manage_uuid';
import ingest from './server/routes/api/ingest';
import search from './server/routes/api/search';
import settings from './server/routes/api/settings';
import scripts from './server/routes/api/scripts';
Expand Down Expand Up @@ -147,7 +146,6 @@ module.exports = function (kibana) {
// uuid
manageUuid(server);
// routes
ingest(server);
search(server);
settings(server);
scripts(server);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import angular from 'angular';
import ngMock from 'ng_mock';
import jQuery from 'jquery';
import expect from 'expect.js';
import sinon from 'sinon';

import createIndexPatternTemplate from '../create_index_pattern.html';
import { StubIndexPatternsApiClientModule } from 'ui/index_patterns/__tests__/stub_index_patterns_api_client';
import { IndexPatternsApiClientProvider } from 'ui/index_patterns';
import MockLogstashFieldsProvider from 'fixtures/logstash_fields';

describe('createIndexPattern UI', () => {
let setup;
const trash = [];

beforeEach(ngMock.module('kibana', StubIndexPatternsApiClientModule, ($provide) => {
$provide.constant('buildSha', 'abc1234');
$provide.constant('$route', {
current: {
params: {},
locals: {
indexPatternIds: []
}
}
});
}));

beforeEach(ngMock.inject(($injector) => {
setup = function () {
const Private = $injector.get('Private');
const Promise = $injector.get('Promise');
const $compile = $injector.get('$compile');
const $rootScope = $injector.get('$rootScope');

const fields = Private(MockLogstashFieldsProvider);
const indexPatternsApiClient = Private(IndexPatternsApiClientProvider);
const $scope = $rootScope.$new();
const $view = jQuery($compile(angular.element('<div>').html(createIndexPatternTemplate))($scope));
trash.push(() => $scope.$destroy());
$scope.$apply();

// prevents errors when switching to time pattern
indexPatternsApiClient.testTimePattern = sinon.spy(() => Promise.resolve({
all: ['logstash-0', 'logstash-2017.01.01'],
matches: ['logstash-2017.01.01'],
}));

const setNameTo = (name) => {
$view.findTestSubject('createIndexPatternNameInput')
.val(name)
.change()
.blur();

// ensure that name successfully applied
const form = $view.find('form').scope().form;
expect(form.name).to.have.property('$viewValue', name);
};

return {
$view,
$scope,
setNameTo,
indexPatternsApiClient,
fields
};
};
}));

afterEach(() => {
trash.forEach(fn => fn());
trash.length = 0;
});

describe('defaults', () => {
it('renders `logstash-*` into the name input', () => {
const { $view } = setup();

const $name = $view.findTestSubject('createIndexPatternNameInput');
expect($name).to.have.length(1);
expect($name.val()).to.be('logstash-*');
});

it('attempts to getFieldsForWildcard for `logstash-*`', () => {
const { indexPatternsApiClient } = setup();
const { getFieldsForWildcard } = indexPatternsApiClient;

sinon.assert.called(getFieldsForWildcard);
const calledWithPattern = getFieldsForWildcard.getCalls().some(call => {
const [params] = call.args;
return (
params &&
params.pattern &&
params.pattern === 'logstash-*'
);
});

if (!calledWithPattern) {
throw new Error('expected indexPatternsApiClient.getFieldsForWildcard to be called with pattern = logstash-*');
}
});

it('loads the time fields into the select box', () => {
const { $view, fields } = setup();

const timeFieldOptions = $view.findTestSubject('createIndexPatternTimeFieldSelect')
.find('option')
.toArray()
.map(option => option.innerText);

fields.forEach((field) => {
if (!field.scripted && field.type === 'date') {
expect(timeFieldOptions).to.contain(field.name);
} else {
expect(timeFieldOptions).to.not.contain(field.name);
}
});
});

it('displays the option (off) to expand wildcards', () => {
const { $view } = setup();
const $enableExpand = $view.findTestSubject('createIndexPatternEnableExpand');
expect($enableExpand).to.have.length(1);
expect($enableExpand.is(':checked')).to.be(false);
});

it('displays the option (off) to use time patterns', () => {
const { $view } = setup();
const $enableTimePattern = $view.findTestSubject('createIndexPatternNameIsPatternCheckBox');
expect($enableTimePattern).to.have.length(1);
expect($enableTimePattern.is(':checked')).to.be(false);
});
});

describe('cross cluster pattern', () => {
it('name input accepts `cluster2:logstash-*` pattern', () => {
const { $view, setNameTo } = setup();
setNameTo('cluster2:logstash-*');

const $name = $view.findTestSubject('createIndexPatternNameInput');
const classes = [...$name.get(0).classList];
expect(classes).to.contain('ng-valid');
expect(classes).to.not.contain('ng-invalid');
});

it('removes the option to expand wildcards', () => {
const { $view, setNameTo } = setup();
setNameTo('cluster2:logstash-*');

const $enableExpand = $view.findTestSubject('createIndexPatternEnableExpand');
expect($enableExpand).to.have.length(0);
});

it('removes the option to use time patterns', () => {
const { $view, setNameTo } = setup();
setNameTo('cluster2:logstash-*');

const $enableTimePattern = $view.findTestSubject('createIndexPatternNameIsPatternCheckBox');
expect($enableTimePattern).to.have.length(0);
});
});

describe('expand selected', () => {
it('removes the option to use time patterns', () => {
const { $view } = setup();

const { controller } = $view.findTestSubject('createIndexPatternContainer').scope();
const $enableExpand = $view.findTestSubject('createIndexPatternEnableExpand');
expect($enableExpand).to.have.length(1);
$enableExpand.click();
expect(controller.isExpandWildcardEnabled()).to.be(true);

const $enableTimePattern = $view.findTestSubject('createIndexPatternNameIsPatternCheckBox');
expect($enableTimePattern).to.have.length(0);
});
});

describe('time pattern selected', () => {
it('removes the option to use wildcard expansion', () => {
const { $view } = setup();

const { controller } = $view.findTestSubject('createIndexPatternContainer').scope();
const $enableTimePattern = $view.findTestSubject('createIndexPatternNameIsPatternCheckBox');
expect($enableTimePattern).to.have.length(1);
$enableTimePattern.click();
expect(controller.formValues.nameIsPattern).to.be(true);

const $enableExpand = $view.findTestSubject('createIndexPatternEnableExpand');
expect($enableExpand).to.have.length(0);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<kbn-management-indices>
<div
ng-controller="managementIndicesCreate as controller"
data-test-subj="createIndexPatternContainer"
class="kuiViewContent"
>
<h1
Expand Down Expand Up @@ -34,8 +35,7 @@
class="kuiTextInput kuiTextInput--large"
data-test-subj="createIndexPatternNameInput"
ng-model="controller.formValues.name"
ng-attr-placeholder="{{controller.formValues.defaultName}}"
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 2500, 'blur': 0} }"
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"
validate-index-name
allow-wildcard
name="name"
Expand Down Expand Up @@ -124,12 +124,13 @@
</div>

<!-- Expand index pattern checkbox -->
<div class="kuiVerticalRhythm" ng-if="controller.canExpandIndices()">
<div class="kuiVerticalRhythm" ng-if="controller.canEnableExpandWildcard()">
<label class="kuiCheckBoxLabel kuiVerticalRhythm">
<input
class="kuiCheckBox"
type="checkbox"
ng-model="controller.formValues.expandable"
data-test-subj="createIndexPatternEnableExpand"
ng-model="controller.formValues.expandWildcard"
>
<span
class="kuiCheckBoxLabel__text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ uiModules.get('apps/management')
this.formValues = {
name: config.get('indexPattern:placeholder'),
nameIsPattern: false,
expandable: false,
expandWildcard: false,
nameInterval: _.find(intervals, { name: 'daily' }),
timeFieldOption: null,
};
Expand All @@ -41,20 +41,20 @@ uiModules.get('apps/management')
this.patternErrors = [];

const getTimeFieldOptions = () => {
const missingPattern = !this.formValues.name;
const missingInterval = this.formValues.nameIsPattern && !this.formValues.nameInterval;
if (missingPattern || missingInterval) {
return Promise.resolve({ options: [] });
}

loadingCount += 1;
return indexPatterns.mapper.clearCache(this.formValues.name)
return Promise.resolve()
.then(() => {
const pattern = mockIndexPattern(this.formValues);
const { nameIsPattern, name } = this.formValues;

return indexPatterns.mapper.getFieldsForIndexPattern(pattern, {
skipIndexPatternCache: true,
});
if (!name) {
return [];
}

if (nameIsPattern) {
return indexPatterns.fieldsFetcher.fetchForTimePattern(name);
}

return indexPatterns.fieldsFetcher.fetchForWildcard(name);
})
.then(fields => {
const dateFields = fields.filter(field => field.type === 'date');
Expand Down Expand Up @@ -132,29 +132,15 @@ uiModules.get('apps/management')
this.existing = null;
};

function mockIndexPattern(index) {
// trick the mapper into thinking this is an indexPattern
return {
id: index.name,
intervalName: index.nameInterval
};
}

const updateSamples = () => {
const patternErrors = [];

if (!this.formValues.nameInterval || !this.formValues.name) {
return Promise.resolve();
}

const pattern = mockIndexPattern(this.formValues);

loadingCount += 1;
return indexPatterns.mapper.getIndicesForIndexPattern(pattern)
.catch(err => {
if (err instanceof IndexPatternMissingIndices) return;
notify.error(err);
})
return indexPatterns.fieldsFetcher.testTimePattern(this.formValues.name)
.then(existing => {
const all = _.get(existing, 'all', []);
const matches = _.get(existing, 'matches', []);
Expand Down Expand Up @@ -197,16 +183,35 @@ uiModules.get('apps/management')
return Boolean(this.formValues.timeFieldOption.fieldName);
};

this.canExpandIndices = () => {
this.canEnableExpandWildcard = () => {
return (
this.isTimeBased() &&
!this.isCrossClusterName() &&
!this.formValues.nameIsPattern &&
_.includes(this.formValues.name, '*')
);
};

this.isExpandWildcardEnabled = () => {
return (
this.canEnableExpandWildcard() &&
!!this.formValues.expandWildcard
);
};

this.canUseTimePattern = () => {
return this.isTimeBased() && !this.formValues.expandable;
return (
this.isTimeBased() &&
!this.isExpandWildcardEnabled() &&
!this.isCrossClusterName()
);
};

this.isCrossClusterName = () => {
return (
this.formValues.name &&
this.formValues.name.includes(':')
);
};

this.isLoading = () => {
Expand Down Expand Up @@ -261,7 +266,6 @@ uiModules.get('apps/management')
timeFieldOption,
nameIsPattern,
nameInterval,
expandable
} = this.formValues;

const id = name;
Expand All @@ -270,10 +274,9 @@ uiModules.get('apps/management')
? timeFieldOption.fieldName
: undefined;

// this seems wrong, but it's the original logic... https://git.io/vHYFo
const notExpandable = (this.canExpandIndices() && !expandable)
? true
: undefined;
const notExpandable = this.isExpandWildcardEnabled()
? undefined
: true;

// Only event-time-based index patterns set an intervalName.
const intervalName = (this.canUseTimePattern() && nameIsPattern && nameInterval)
Expand Down
Loading

0 comments on commit 18f63d2

Please sign in to comment.