Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IndexPatterns] Support cross cluster search #11114

Merged
merged 20 commits into from
Jun 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3de0b0a
[indexPatterns] support cross cluster patterns
spalger May 26, 2017
7f53d54
[vis] remove unused `hasTimeField` param
spalger May 26, 2017
774c129
[indexPatterns/create] fix method name in view
spalger May 26, 2017
6a2058a
[indexPatterns/create] disallow expanding with ccs
spalger May 26, 2017
d99420e
[indexPatterns/create] field fetching is cheaper, react faster
spalger May 26, 2017
f37b368
Merge branch 'master' of github.com:elastic/kibana into implement/fie…
spalger May 26, 2017
d7aa7e4
[indexPatterns/resolveTimePattern/tests] increase readability
spalger May 26, 2017
2993304
[tests/apiIntegration/indexPatterns] test conflict field output
spalger May 26, 2017
a6fe02b
[indexPatterns/fieldCaps/readFieldCapsResponse] add unit tests
spalger May 26, 2017
e693a10
Merge branch 'master' of github.com:elastic/kibana into implement/fie…
spalger May 27, 2017
717b455
Merge branch 'master' of github.com:elastic/kibana into implement/fie…
spalger May 30, 2017
cfb52d4
[test/apiIntegration] ensure random word will not be valid
spalger May 30, 2017
62727b4
Merge branch 'master' of github.com:elastic/kibana into implement/fie…
spalger May 31, 2017
dabc005
[indexPatterns/ui/client] remove unused import
spalger May 31, 2017
c038dcc
Merge branch 'master' of github.com:elastic/kibana into implement/fie…
spalger Jun 1, 2017
a687ee1
remove use of auto-release-sinon
spalger Jun 1, 2017
4e74bf4
Merge branch 'master' of github.com:elastic/kibana into implement/fie…
spalger Jun 2, 2017
acc2e7e
[indexPatterns/create] don't allow expand when cross cluster
spalger Jun 2, 2017
6fd438c
[indexPatternsApiClient/stub] use angular promises
spalger Jun 2, 2017
47b2387
[indexPatterns/create] add tests for base create ui behaviors
spalger Jun 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -123,7 +122,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