[indexPatterns] remove support for time patterns (#12158)
* [indexPatterns] remove support for time patterns
* Revert "[indexPatterns] remove support for time patterns"
This reverts commit 4263e37c66
.
* [indexPatterns] remove ability to create time-based patterns
* [indexPattern/routes] fix export of routes for stub
* [Storage] export Storage class for testing
* [indexPatterns/unsupportedTimePatterns] add tests
* [indexPatterns] focus warning check module
* [indexPatterns/tests] fix method name
* add metion of this change to migration docs
* disable warnings by default until we have a migration tool
* prevent the warning from disapearing
* fix grammar
* enabled warnings in the tests
This commit is contained in:
parent
2a0ed8e367
commit
1119414b49
|
@ -52,3 +52,9 @@ This is no longer the case. Now, only commas are a valid query separator: e.g. `
|
||||||
*Details:* In Kibana 4.2, we renamed all configuration names in kibana.yml to use `.` as a separator instead of `_`, though the legacy configurations would still continue to work. In 5.0, we started logging deprecation notices whenever the legacy configurations were encountered. In 6.0 onward, legacy configuration names that use an underscore instead of a dot will no longer work.
|
*Details:* In Kibana 4.2, we renamed all configuration names in kibana.yml to use `.` as a separator instead of `_`, though the legacy configurations would still continue to work. In 5.0, we started logging deprecation notices whenever the legacy configurations were encountered. In 6.0 onward, legacy configuration names that use an underscore instead of a dot will no longer work.
|
||||||
|
|
||||||
*Impact:* Any usages of underscore separated configuration names in kibana.yml need to be updated to their modern equivalents. See <<settings,Configuring Kibana>> for accepted configurations.
|
*Impact:* Any usages of underscore separated configuration names in kibana.yml need to be updated to their modern equivalents. See <<settings,Configuring Kibana>> for accepted configurations.
|
||||||
|
|
||||||
|
[float]
|
||||||
|
=== Time-interval based index patterns are no longer supported
|
||||||
|
*Details:* Starting in Kibana 6.0.0 we removed the ability to create index patterns that use a date-pattern and interval to identify Elasticsearch indices. Index patterns must now use wildcards which are more performant in most cases.
|
||||||
|
|
||||||
|
*Impact:* Existing index patterns and saved objects will continue to function without issue, and in a subsequent release we will provide utilities to migrate your index patterns/saved objects.
|
|
@ -28,7 +28,6 @@ describe('createIndexPattern UI', () => {
|
||||||
beforeEach(ngMock.inject(($injector) => {
|
beforeEach(ngMock.inject(($injector) => {
|
||||||
setup = function () {
|
setup = function () {
|
||||||
const Private = $injector.get('Private');
|
const Private = $injector.get('Private');
|
||||||
const Promise = $injector.get('Promise');
|
|
||||||
const $compile = $injector.get('$compile');
|
const $compile = $injector.get('$compile');
|
||||||
const $rootScope = $injector.get('$rootScope');
|
const $rootScope = $injector.get('$rootScope');
|
||||||
|
|
||||||
|
@ -39,12 +38,6 @@ describe('createIndexPattern UI', () => {
|
||||||
trash.push(() => $scope.$destroy());
|
trash.push(() => $scope.$destroy());
|
||||||
$scope.$apply();
|
$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) => {
|
const setNameTo = (name) => {
|
||||||
$view.findTestSubject('createIndexPatternNameInput')
|
$view.findTestSubject('createIndexPatternNameInput')
|
||||||
.val(name)
|
.val(name)
|
||||||
|
@ -122,13 +115,6 @@ describe('createIndexPattern UI', () => {
|
||||||
expect($enableExpand).to.have.length(1);
|
expect($enableExpand).to.have.length(1);
|
||||||
expect($enableExpand.is(':checked')).to.be(false);
|
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', () => {
|
describe('cross cluster pattern', () => {
|
||||||
|
@ -149,43 +135,5 @@ describe('createIndexPattern UI', () => {
|
||||||
const $enableExpand = $view.findTestSubject('createIndexPatternEnableExpand');
|
const $enableExpand = $view.findTestSubject('createIndexPatternEnableExpand');
|
||||||
expect($enableExpand).to.have.length(0);
|
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -61,37 +61,8 @@
|
||||||
<div class="kuiVerticalRhythm">
|
<div class="kuiVerticalRhythm">
|
||||||
<p
|
<p
|
||||||
class="kuiSubText kuiVerticalRhythmSmall"
|
class="kuiSubText kuiVerticalRhythmSmall"
|
||||||
ng-if="!controller.formValues.nameIsPattern"
|
|
||||||
translate="KIBANA-WILDCARD_DYNAMIC_INDEX_PATTERNS"
|
translate="KIBANA-WILDCARD_DYNAMIC_INDEX_PATTERNS"
|
||||||
></p>
|
></p>
|
||||||
|
|
||||||
<p
|
|
||||||
class="kuiSubText kuiVerticalRhythmSmall"
|
|
||||||
ng-if="controller.formValues.nameIsPattern"
|
|
||||||
>
|
|
||||||
<span translate="KIBANA-STATIC_TEXT_IN_DYNAMIC_INDEX_PATTERNS"></span> —
|
|
||||||
<a
|
|
||||||
class="kuiLink"
|
|
||||||
href="http://momentjs.com/docs/#/displaying/format/"
|
|
||||||
target="_blank"
|
|
||||||
translate="KIBANA-DATE_FORMAT_DOCS"
|
|
||||||
></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p
|
|
||||||
class="kuiSubText kuiVerticalRhythmSmall"
|
|
||||||
ng-show="controller.formValues.nameInterval.name == 'weeks'"
|
|
||||||
>
|
|
||||||
<strong translate="KIBANA-NOTE_COLON"></strong>
|
|
||||||
<span translate="KIBANA-WEEKLY_ISO_NOTICE"></span>
|
|
||||||
<span translate="KIBANA-SEE"></span>
|
|
||||||
<a
|
|
||||||
class="kuiLink"
|
|
||||||
href="https://en.wikipedia.org/wiki/ISO_week_date"
|
|
||||||
target="_blank"
|
|
||||||
translate="KIBANA-WIKI_ISO_WEEK_DATE"
|
|
||||||
></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -160,188 +131,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Use event times checkbox -->
|
|
||||||
<div
|
|
||||||
class="kuiVerticalRhythm"
|
|
||||||
ng-if="controller.canUseTimePattern()"
|
|
||||||
>
|
|
||||||
<label class="kuiCheckBoxLabel">
|
|
||||||
<input
|
|
||||||
class="kuiCheckBox"
|
|
||||||
type="checkbox"
|
|
||||||
data-test-subj="createIndexPatternNameIsPatternCheckBox"
|
|
||||||
ng-model="controller.formValues.nameIsPattern"
|
|
||||||
>
|
|
||||||
|
|
||||||
<span class="kuiCheckBoxLabel__text">
|
|
||||||
<span translate="KIBANA-INDEX_NAME_CREATED_BY_EVENT_TIMES"></span>
|
|
||||||
<span translate="KIBANA-DEPRECATED"></span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="kuiVerticalRhythm"
|
|
||||||
ng-if="controller.canUseTimePattern() && controller.formValues.nameIsPattern"
|
|
||||||
>
|
|
||||||
<!-- Time-interval deprecation warning -->
|
|
||||||
<div class="kuiInfoPanel kuiInfoPanel--warning kuiVerticalRhythm">
|
|
||||||
<div class="kuiInfoPanelHeader">
|
|
||||||
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--warning fa-bolt"></span>
|
|
||||||
<span
|
|
||||||
class="kuiInfoPanelHeader__title"
|
|
||||||
translate="KIBANA-ALERT_INDEX_PATTERN_DEPRECATED"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody">
|
|
||||||
<div class="kuiInfoPanelBody__message">
|
|
||||||
<span translate="KIBANA-WE"></span>
|
|
||||||
<strong translate="KIBANA-STRONGLY_RECOMMEND"></strong>
|
|
||||||
<span translate="KIBANA-WILD_CARD_PATTERN"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Index pattern interval select -->
|
|
||||||
<div class="kuiVerticalRhythm">
|
|
||||||
<label class="kuiLabel kuiVerticalRhythmSmall">
|
|
||||||
<span translate="KIBANA-INDEX_PATTERN_INTERVAL"></span>
|
|
||||||
<kbn-info info="{{ 'KIBANA-INTERVAL_INDEX_NAMES_ROTATE' | translate }}"></kbn-info>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="kuiVerticalRhythmSmall">
|
|
||||||
<select
|
|
||||||
class="kuiSelect"
|
|
||||||
required
|
|
||||||
ng-options="opt.display for opt in controller.nameIntervalOptions"
|
|
||||||
ng-model="controller.formValues.nameInterval"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Errors -->
|
|
||||||
<div class="kuiVerticalRhythm">
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanel kuiInfoPanel--error kuiVerticalRhythmSmall"
|
|
||||||
ng-repeat="err in controller.patternErrors"
|
|
||||||
>
|
|
||||||
<div class="kuiInfoPanelHeader">
|
|
||||||
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--error fa-warning"></span>
|
|
||||||
<span class="kuiInfoPanelHeader__title">
|
|
||||||
{{err}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanel kuiInfoPanel--info kuiVerticalRhythmSmall"
|
|
||||||
ng-if="controller.samples"
|
|
||||||
>
|
|
||||||
<div class="kuiInfoPanelHeader">
|
|
||||||
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--info fa-info"></span>
|
|
||||||
<span
|
|
||||||
class="kuiInfoPanelHeader__title"
|
|
||||||
translate="KIBANA-SAMPLE_ALERT"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody">
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanelBody__message"
|
|
||||||
ng-repeat="sample in controller.samples"
|
|
||||||
>
|
|
||||||
{{sample}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody__message">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
ng-click="controller.moreSamples(true)"
|
|
||||||
class="kuiButton kuiButton--basic"
|
|
||||||
>
|
|
||||||
<span translate="KIBANA-EXPAND_SEARCH"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Percentage of indices which match the index pattern -->
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanel kuiVerticalRhythmSmall"
|
|
||||||
ng-class="controller.existing.matches.length ? 'kuiInfoPanel--success' : 'kuiInfoPanel--error'"
|
|
||||||
ng-if="controller.existing"
|
|
||||||
>
|
|
||||||
<div class="kuiInfoPanelHeader">
|
|
||||||
<span
|
|
||||||
class="kuiInfoPanelHeader__icon kuiIcon"
|
|
||||||
ng-class="controller.existing.matches.length ? 'kuiIcon--success fa-check' : 'kuiIcon--error fa-warning'"
|
|
||||||
></span>
|
|
||||||
<span
|
|
||||||
class="kuiInfoPanelHeader__title"
|
|
||||||
translate="KIBANA-EXISTING_MATCH_PERCENT"
|
|
||||||
translate-values="{ indexExistingMatchPercent: '{{controller.existing.matchPercent}}' }"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanelBody"
|
|
||||||
ng-if="controller.existing.matches.length"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanelBody__message"
|
|
||||||
ng-repeat="match in controller.existing.matches | orderBy:'toString()'| limitTo: controller.sampleCount"
|
|
||||||
>
|
|
||||||
{{match}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody__message">
|
|
||||||
<button
|
|
||||||
ng-if="controller.sampleCount < controller.existing.matches.length"
|
|
||||||
ng-click="controller.moreSamples()"
|
|
||||||
type="button"
|
|
||||||
class="kuiButton kuiButton--basic"
|
|
||||||
>
|
|
||||||
<span translate="KIBANA-EXPAND_SEARCH"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanel kuiInfoPanel--info kuiVerticalRhythmSmall"
|
|
||||||
ng-if="controller.existing.failures.length"
|
|
||||||
>
|
|
||||||
<div class="kuiInfoPanelHeader">
|
|
||||||
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--info fa-info"></span>
|
|
||||||
<span
|
|
||||||
class="kuiInfoPanelHeader__title"
|
|
||||||
translate="KIBANA-NON_MATCHING_INDICES_AND_ALIASES"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody">
|
|
||||||
<div
|
|
||||||
class="kuiInfoPanelBody__message"
|
|
||||||
ng-repeat="match in controller.existing.failures | limitTo: controller.sampleCount"
|
|
||||||
>
|
|
||||||
{{match}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody__message">
|
|
||||||
<a
|
|
||||||
class="kuiLink"
|
|
||||||
ng-if="controller.sampleCount < controller.existing.matches.length"
|
|
||||||
ng-click="controller.moreSamples()"
|
|
||||||
>
|
|
||||||
<span translate="KIBANA-MORE"></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Form actions -->
|
<!-- Form actions -->
|
||||||
<button
|
<button
|
||||||
data-test-subj="createIndexPatternCreateButton"
|
data-test-subj="createIndexPatternCreateButton"
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { RefreshKibanaIndex } from '../refresh_kibana_index';
|
||||||
import uiRoutes from 'ui/routes';
|
import uiRoutes from 'ui/routes';
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
import template from './create_index_pattern.html';
|
import template from './create_index_pattern.html';
|
||||||
import { getDefaultPatternForInterval } from './get_default_pattern_for_interval';
|
|
||||||
import { sendCreateIndexPatternRequest } from './send_create_index_pattern_request';
|
import { sendCreateIndexPatternRequest } from './send_create_index_pattern_request';
|
||||||
import { pickCreateButtonText } from './pick_create_button_text';
|
import { pickCreateButtonText } from './pick_create_button_text';
|
||||||
|
|
||||||
|
@ -19,41 +18,27 @@ uiModules.get('apps/management')
|
||||||
.controller('managementIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise, $translate) {
|
.controller('managementIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise, $translate) {
|
||||||
const notify = new Notifier();
|
const notify = new Notifier();
|
||||||
const refreshKibanaIndex = Private(RefreshKibanaIndex);
|
const refreshKibanaIndex = Private(RefreshKibanaIndex);
|
||||||
const intervals = indexPatterns.intervals;
|
|
||||||
let loadingCount = 0;
|
let loadingCount = 0;
|
||||||
|
|
||||||
// Configure the new index pattern we're going to create.
|
// Configure the new index pattern we're going to create.
|
||||||
this.formValues = {
|
this.formValues = {
|
||||||
name: config.get('indexPattern:placeholder'),
|
name: config.get('indexPattern:placeholder'),
|
||||||
nameIsPattern: false,
|
|
||||||
expandWildcard: false,
|
expandWildcard: false,
|
||||||
nameInterval: _.find(intervals, { name: 'daily' }),
|
|
||||||
timeFieldOption: null,
|
timeFieldOption: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// UI state.
|
// UI state.
|
||||||
this.timeFieldOptions = [];
|
this.timeFieldOptions = [];
|
||||||
this.timeFieldOptionsError = null;
|
this.timeFieldOptionsError = null;
|
||||||
this.sampleCount = 5;
|
|
||||||
this.samples = null;
|
|
||||||
this.existing = null;
|
|
||||||
this.nameIntervalOptions = intervals;
|
|
||||||
this.patternErrors = [];
|
|
||||||
|
|
||||||
const getTimeFieldOptions = () => {
|
const getTimeFieldOptions = () => {
|
||||||
loadingCount += 1;
|
loadingCount += 1;
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const { nameIsPattern, name } = this.formValues;
|
const { name } = this.formValues;
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nameIsPattern) {
|
|
||||||
return indexPatterns.fieldsFetcher.fetchForTimePattern(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return indexPatterns.fieldsFetcher.fetchForWildcard(name);
|
return indexPatterns.fieldsFetcher.fetchForWildcard(name);
|
||||||
})
|
})
|
||||||
.then(fields => {
|
.then(fields => {
|
||||||
|
@ -126,51 +111,6 @@ uiModules.get('apps/management')
|
||||||
return nonFieldOptions[0];
|
return nonFieldOptions[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetIndex = () => {
|
|
||||||
this.patternErrors = [];
|
|
||||||
this.samples = null;
|
|
||||||
this.existing = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateSamples = () => {
|
|
||||||
const patternErrors = [];
|
|
||||||
|
|
||||||
if (!this.formValues.nameInterval || !this.formValues.name) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingCount += 1;
|
|
||||||
return indexPatterns.fieldsFetcher.testTimePattern(this.formValues.name)
|
|
||||||
.then(existing => {
|
|
||||||
const all = _.get(existing, 'all', []);
|
|
||||||
const matches = _.get(existing, 'matches', []);
|
|
||||||
|
|
||||||
if (all.length) {
|
|
||||||
return this.existing = {
|
|
||||||
all,
|
|
||||||
matches,
|
|
||||||
matchPercent: Math.round((matches.length / all.length) * 100) + '%',
|
|
||||||
failures: _.difference(all, matches)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
patternErrors.push($translate.instant('KIBANA-PATTERN_DOES_NOT_MATCH_EXIST_INDICES'));
|
|
||||||
const radius = Math.round(this.sampleCount / 2);
|
|
||||||
const samples = intervals.toIndexList(this.formValues.name, this.formValues.nameInterval, -radius, radius);
|
|
||||||
|
|
||||||
if (_.uniq(samples).length !== samples.length) {
|
|
||||||
patternErrors.push($translate.instant('KIBANA-INVALID_NON_UNIQUE_INDEX_NAME_CREATED'));
|
|
||||||
} else {
|
|
||||||
this.samples = samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw patternErrors;
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
loadingCount -= 1;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isTimeBased = () => {
|
this.isTimeBased = () => {
|
||||||
if (!this.formValues.timeFieldOption) {
|
if (!this.formValues.timeFieldOption) {
|
||||||
// if they haven't choosen a time field, assume they will
|
// if they haven't choosen a time field, assume they will
|
||||||
|
@ -187,7 +127,6 @@ uiModules.get('apps/management')
|
||||||
return (
|
return (
|
||||||
this.isTimeBased() &&
|
this.isTimeBased() &&
|
||||||
!this.isCrossClusterName() &&
|
!this.isCrossClusterName() &&
|
||||||
!this.formValues.nameIsPattern &&
|
|
||||||
_.includes(this.formValues.name, '*')
|
_.includes(this.formValues.name, '*')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -199,14 +138,6 @@ uiModules.get('apps/management')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.canUseTimePattern = () => {
|
|
||||||
return (
|
|
||||||
this.isTimeBased() &&
|
|
||||||
!this.isExpandWildcardEnabled() &&
|
|
||||||
!this.isCrossClusterName()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isCrossClusterName = () => {
|
this.isCrossClusterName = () => {
|
||||||
return (
|
return (
|
||||||
this.formValues.name &&
|
this.formValues.name &&
|
||||||
|
@ -264,8 +195,6 @@ uiModules.get('apps/management')
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
timeFieldOption,
|
timeFieldOption,
|
||||||
nameIsPattern,
|
|
||||||
nameInterval,
|
|
||||||
} = this.formValues;
|
} = this.formValues;
|
||||||
|
|
||||||
const id = name;
|
const id = name;
|
||||||
|
@ -278,16 +207,10 @@ uiModules.get('apps/management')
|
||||||
? undefined
|
? undefined
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
// Only event-time-based index patterns set an intervalName.
|
|
||||||
const intervalName = (this.canUseTimePattern() && nameIsPattern && nameInterval)
|
|
||||||
? nameInterval.name
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
loadingCount += 1;
|
loadingCount += 1;
|
||||||
sendCreateIndexPatternRequest(indexPatterns, {
|
sendCreateIndexPatternRequest(indexPatterns, {
|
||||||
id,
|
id,
|
||||||
timeFieldName,
|
timeFieldName,
|
||||||
intervalName,
|
|
||||||
notExpandable,
|
notExpandable,
|
||||||
}).then(createdId => {
|
}).then(createdId => {
|
||||||
if (!createdId) {
|
if (!createdId) {
|
||||||
|
@ -316,65 +239,7 @@ uiModules.get('apps/management')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$watchMulti([
|
$scope.$watch('controller.formValues.name', () => {
|
||||||
'controller.formValues.nameIsPattern',
|
|
||||||
'controller.formValues.nameInterval.name',
|
|
||||||
], (newVal, oldVal) => {
|
|
||||||
const nameIsPattern = newVal[0];
|
|
||||||
const newDefault = getDefaultPatternForInterval(newVal[1]);
|
|
||||||
const oldDefault = getDefaultPatternForInterval(oldVal[1]);
|
|
||||||
|
|
||||||
if (this.formValues.name === oldDefault) {
|
|
||||||
this.formValues.name = newDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nameIsPattern) {
|
|
||||||
delete this.formValues.nameInterval;
|
|
||||||
} else {
|
|
||||||
this.formValues.nameInterval = this.formValues.nameInterval || intervals.byName.days;
|
|
||||||
this.formValues.name = this.formValues.name || getDefaultPatternForInterval(this.formValues.nameInterval);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.moreSamples = andUpdate => {
|
|
||||||
this.sampleCount += 5;
|
|
||||||
if (andUpdate) updateSamples();
|
|
||||||
};
|
|
||||||
|
|
||||||
let latestUpdateSampleId = -1;
|
|
||||||
$scope.$watchMulti([
|
|
||||||
'controller.formValues.name',
|
|
||||||
'controller.formValues.nameInterval'
|
|
||||||
], () => {
|
|
||||||
resetIndex();
|
|
||||||
|
|
||||||
// track the latestUpdateSampleId at the time we started
|
|
||||||
// so that we can avoid mutating the controller if the
|
|
||||||
// watcher triggers again before we finish (which would
|
|
||||||
// cause latestUpdateSampleId to increment and the
|
|
||||||
// id === latestUpdateSampleId checks below to fail)
|
|
||||||
const id = (++latestUpdateSampleId);
|
|
||||||
updateSamples()
|
|
||||||
.then(() => {
|
|
||||||
if (latestUpdateSampleId === id) {
|
|
||||||
this.samples = null;
|
|
||||||
this.patternErrors = [];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(errors => {
|
|
||||||
if (latestUpdateSampleId === id) {
|
|
||||||
this.existing = null;
|
|
||||||
this.patternErrors = errors;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.refreshTimeFieldOptions();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.$watchMulti([
|
|
||||||
'controller.sampleCount'
|
|
||||||
], () => {
|
|
||||||
this.refreshTimeFieldOptions();
|
this.refreshTimeFieldOptions();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export function sendCreateIndexPatternRequest(indexPatterns, {
|
export function sendCreateIndexPatternRequest(indexPatterns, {
|
||||||
id,
|
id,
|
||||||
timeFieldName,
|
timeFieldName,
|
||||||
intervalName,
|
|
||||||
notExpandable,
|
notExpandable,
|
||||||
}) {
|
}) {
|
||||||
// get an empty indexPattern to start
|
// get an empty indexPattern to start
|
||||||
|
@ -12,7 +11,6 @@ export function sendCreateIndexPatternRequest(indexPatterns, {
|
||||||
|
|
||||||
Object.assign(indexPattern, {
|
Object.assign(indexPattern, {
|
||||||
timeFieldName,
|
timeFieldName,
|
||||||
intervalName,
|
|
||||||
notExpandable,
|
notExpandable,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -32,19 +32,21 @@
|
||||||
|
|
||||||
<!-- Alerts -->
|
<!-- Alerts -->
|
||||||
<div
|
<div
|
||||||
ng-if="indexPattern.timeFieldName && indexPattern.intervalName"
|
ng-if="indexPattern.isUnsupportedTimePattern()"
|
||||||
class="kuiInfoPanel kuiInfoPanel--warning kuiVerticalRhythm"
|
class="kuiInfoPanel kuiInfoPanel--error kuiVerticalRhythm"
|
||||||
>
|
>
|
||||||
<div class="kuiInfoPanelHeader">
|
<div class="kuiInfoPanelHeader">
|
||||||
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--warning fa-bolt"></span>
|
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--error fa-warning"></span>
|
||||||
<span class="kuiInfoPanelHeader__title">
|
<span class="kuiInfoPanelHeader__title">
|
||||||
Repeating index pattern
|
Support for repeating index patterns removed
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody">
|
<div class="kuiInfoPanelBody">
|
||||||
<div class="kuiInfoPanelBody__message">
|
<div class="kuiInfoPanelBody__message">
|
||||||
This index uses a <strong>time-based index pattern</strong> which repeats <span ng-bind="::indexPattern.getInterval().display"></span>
|
Support for time-interval based index patterns has been removed! In the next major
|
||||||
|
version of Kibana this index patterns will stop working. Migrate saved objects that
|
||||||
|
use this index pattern to a wildcard pattern and delete this one.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -182,6 +182,11 @@ export function getUiSettingDefaults() {
|
||||||
description: 'For index patterns containing timestamps in their names, look for this many recent matching ' +
|
description: 'For index patterns containing timestamps in their names, look for this many recent matching ' +
|
||||||
'patterns from which to query the field mapping'
|
'patterns from which to query the field mapping'
|
||||||
},
|
},
|
||||||
|
'indexPatterns:warnAboutUnsupportedTimePatterns': {
|
||||||
|
value: false,
|
||||||
|
description: 'When an index pattern is using the now unsupported "time pattern" format, a warning will ' +
|
||||||
|
'be displayed once per session that is using this pattern. Set this to false to disable that warning.'
|
||||||
|
},
|
||||||
'format:defaultTypeMap': {
|
'format:defaultTypeMap': {
|
||||||
type: 'json',
|
type: 'json',
|
||||||
value:
|
value:
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { IndexPatternsService } from './service';
|
import { IndexPatternsService } from './service';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createTestTimePatternRoute,
|
|
||||||
createFieldsForWildcardRoute,
|
createFieldsForWildcardRoute,
|
||||||
createFieldsForTimePatternRoute,
|
createFieldsForTimePatternRoute,
|
||||||
} from './routes';
|
} from './routes';
|
||||||
|
@ -25,7 +24,6 @@ export function indexPatternsMixin(kbnServer, server) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
server.route(createTestTimePatternRoute(pre));
|
|
||||||
server.route(createFieldsForWildcardRoute(pre));
|
server.route(createFieldsForWildcardRoute(pre));
|
||||||
server.route(createFieldsForTimePatternRoute(pre));
|
server.route(createFieldsForTimePatternRoute(pre));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
export { createTestTimePatternRoute } from './test_time_pattern_route';
|
|
||||||
export { createFieldsForWildcardRoute } from './fields_for_wildcard_route';
|
export { createFieldsForWildcardRoute } from './fields_for_wildcard_route';
|
||||||
export { createFieldsForTimePatternRoute } from './fields_for_time_pattern_route';
|
export { createFieldsForTimePatternRoute } from './fields_for_time_pattern_route';
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import Joi from 'joi';
|
|
||||||
|
|
||||||
export const createTestTimePatternRoute = pre => ({
|
|
||||||
path: '/api/index_patterns/_test_time_pattern',
|
|
||||||
method: 'GET',
|
|
||||||
config: {
|
|
||||||
pre: [pre.getIndexPatternsService],
|
|
||||||
validate: {
|
|
||||||
query: Joi.object().keys({
|
|
||||||
pattern: Joi.string().required()
|
|
||||||
}).default()
|
|
||||||
},
|
|
||||||
handler(req, reply) {
|
|
||||||
const { indexPatterns } = req.pre;
|
|
||||||
const { pattern } = req.query;
|
|
||||||
|
|
||||||
reply(indexPatterns.testTimePattern({
|
|
||||||
pattern,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -2,7 +2,6 @@ import {
|
||||||
getFieldCapabilities,
|
getFieldCapabilities,
|
||||||
resolveTimePattern,
|
resolveTimePattern,
|
||||||
createNoMatchingIndicesError,
|
createNoMatchingIndicesError,
|
||||||
isNoMatchingIndicesError,
|
|
||||||
} from './lib';
|
} from './lib';
|
||||||
|
|
||||||
export class IndexPatternsService {
|
export class IndexPatternsService {
|
||||||
|
@ -24,33 +23,6 @@ export class IndexPatternsService {
|
||||||
return await getFieldCapabilities(this._callDataCluster, pattern, metaFields);
|
return await getFieldCapabilities(this._callDataCluster, pattern, metaFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a time pattern into a list of indexes it could
|
|
||||||
* have matched and ones it did match.
|
|
||||||
*
|
|
||||||
* @param {Object} [options={}]
|
|
||||||
* @property {String} options.pattern The moment compatible time pattern
|
|
||||||
* @return {Promise<Object>} object that lists the indices that match based
|
|
||||||
* on a wildcard version of the time pattern (all)
|
|
||||||
* and the indices that actually match the time
|
|
||||||
* pattern (matches);
|
|
||||||
*/
|
|
||||||
async testTimePattern(options = {}) {
|
|
||||||
const { pattern } = options;
|
|
||||||
try {
|
|
||||||
return await resolveTimePattern(this._callDataCluster, pattern);
|
|
||||||
} catch (err) {
|
|
||||||
if (isNoMatchingIndicesError(err)) {
|
|
||||||
return {
|
|
||||||
all: [],
|
|
||||||
matches: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of field objects for a time pattern
|
* Get a list of field objects for a time pattern
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export { getFieldCapabilities } from './field_capabilities';
|
export { getFieldCapabilities } from './field_capabilities';
|
||||||
export { resolveTimePattern } from './resolve_time_pattern';
|
export { resolveTimePattern } from './resolve_time_pattern';
|
||||||
export { createNoMatchingIndicesError, isNoMatchingIndicesError } from './errors';
|
export { createNoMatchingIndicesError } from './errors';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import { IndexPatternProvider } from 'ui/index_patterns/_index_pattern';
|
import { IndexPatternProvider, getRoutes } from 'ui/index_patterns/_index_pattern';
|
||||||
import { formatHit } from 'ui/index_patterns/_format_hit';
|
import { formatHit } from 'ui/index_patterns/_format_hit';
|
||||||
import { getComputedFields } from 'ui/index_patterns/_get_computed_fields';
|
import { getComputedFields } from 'ui/index_patterns/_get_computed_fields';
|
||||||
import { RegistryFieldFormatsProvider } from 'ui/registry/field_formats';
|
import { RegistryFieldFormatsProvider } from 'ui/registry/field_formats';
|
||||||
|
@ -23,7 +23,7 @@ export default function (Private) {
|
||||||
this.getSourceFiltering = sinon.stub();
|
this.getSourceFiltering = sinon.stub();
|
||||||
this.metaFields = ['_id', '_type', '_source'];
|
this.metaFields = ['_id', '_type', '_source'];
|
||||||
this.fieldFormatMap = {};
|
this.fieldFormatMap = {};
|
||||||
this.routes = IndexPatternProvider.routes;
|
this.routes = getRoutes();
|
||||||
|
|
||||||
this.toIndexList = _.constant(Promise.resolve(pattern.split(',')));
|
this.toIndexList = _.constant(Promise.resolve(pattern.split(',')));
|
||||||
this.toDetailedIndexList = _.constant(Promise.resolve(pattern.split(',').map(index => ({
|
this.toDetailedIndexList = _.constant(Promise.resolve(pattern.split(',').map(index => ({
|
||||||
|
|
|
@ -12,11 +12,15 @@ import UtilsMappingSetupProvider from 'ui/utils/mapping_setup';
|
||||||
import { IndexPatternsIntervalsProvider } from 'ui/index_patterns/_intervals';
|
import { IndexPatternsIntervalsProvider } from 'ui/index_patterns/_intervals';
|
||||||
import { IndexPatternProvider } from 'ui/index_patterns/_index_pattern';
|
import { IndexPatternProvider } from 'ui/index_patterns/_index_pattern';
|
||||||
import NoDigestPromises from 'test_utils/no_digest_promises';
|
import NoDigestPromises from 'test_utils/no_digest_promises';
|
||||||
|
import { Notifier } from 'ui/notify';
|
||||||
|
|
||||||
import { FieldsFetcherProvider } from '../fields_fetcher_provider';
|
import { FieldsFetcherProvider } from '../fields_fetcher_provider';
|
||||||
import { StubIndexPatternsApiClientModule } from './stub_index_patterns_api_client';
|
import { StubIndexPatternsApiClientModule } from './stub_index_patterns_api_client';
|
||||||
import { IndexPatternsApiClientProvider } from '../index_patterns_api_client_provider';
|
import { IndexPatternsApiClientProvider } from '../index_patterns_api_client_provider';
|
||||||
import { IndexPatternsCalculateIndicesProvider } from '../_calculate_indices';
|
import { IndexPatternsCalculateIndicesProvider } from '../_calculate_indices';
|
||||||
|
import { IsUserAwareOfUnsupportedTimePatternProvider } from '../unsupported_time_patterns';
|
||||||
|
|
||||||
|
const MARKDOWN_LINK_RE = /\[(.+?)\]\((.+?)\)/;
|
||||||
|
|
||||||
describe('index pattern', function () {
|
describe('index pattern', function () {
|
||||||
NoDigestPromises.activateForSuite();
|
NoDigestPromises.activateForSuite();
|
||||||
|
@ -33,6 +37,7 @@ describe('index pattern', function () {
|
||||||
let intervals;
|
let intervals;
|
||||||
let indexPatternsApiClient;
|
let indexPatternsApiClient;
|
||||||
let defaultTimeField;
|
let defaultTimeField;
|
||||||
|
let isUserAwareOfUnsupportedTimePattern;
|
||||||
|
|
||||||
beforeEach(ngMock.module('kibana', StubIndexPatternsApiClientModule, (PrivateProvider) => {
|
beforeEach(ngMock.module('kibana', StubIndexPatternsApiClientModule, (PrivateProvider) => {
|
||||||
PrivateProvider.swap(IndexPatternsCalculateIndicesProvider, () => {
|
PrivateProvider.swap(IndexPatternsCalculateIndicesProvider, () => {
|
||||||
|
@ -46,6 +51,11 @@ describe('index pattern', function () {
|
||||||
|
|
||||||
return calculateIndices;
|
return calculateIndices;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
isUserAwareOfUnsupportedTimePattern = sinon.stub().returns(false);
|
||||||
|
PrivateProvider.swap(IsUserAwareOfUnsupportedTimePatternProvider, () => {
|
||||||
|
return isUserAwareOfUnsupportedTimePattern;
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(ngMock.inject(function (Private) {
|
beforeEach(ngMock.inject(function (Private) {
|
||||||
|
@ -488,4 +498,46 @@ describe('index pattern', function () {
|
||||||
expect(indexPattern.isWildcard()).to.be(false);
|
expect(indexPattern.isWildcard()).to.be(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#isUnsupportedTimePattern()', () => {
|
||||||
|
it('returns true when intervalName is set', () => {
|
||||||
|
indexPattern.intervalName = 'something';
|
||||||
|
expect(indexPattern.isUnsupportedTimePattern()).to.be(true);
|
||||||
|
});
|
||||||
|
it('returns false otherwise', () => {
|
||||||
|
delete indexPattern.intervalName;
|
||||||
|
expect(indexPattern.isUnsupportedTimePattern()).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unsupported time pattern warning', () => {
|
||||||
|
async function createUnsupportedTimePattern() {
|
||||||
|
return await create('pattern-id', {
|
||||||
|
_source: {
|
||||||
|
timeFieldName: '@timestamp',
|
||||||
|
intervalName: 'days',
|
||||||
|
fields: '[]'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('logs a warning when the index pattern source includes `intervalName`', async () => {
|
||||||
|
const indexPattern = await createUnsupportedTimePattern();
|
||||||
|
expect(Notifier.prototype._notifs).to.have.length(1);
|
||||||
|
const notif = Notifier.prototype._notifs.shift();
|
||||||
|
|
||||||
|
expect(notif).to.have.property('type', 'warning');
|
||||||
|
expect(notif.content).to.match(MARKDOWN_LINK_RE);
|
||||||
|
const [,text,url] = notif.content.match(MARKDOWN_LINK_RE);
|
||||||
|
expect(text).to.contain(indexPattern.id);
|
||||||
|
expect(url).to.contain(indexPattern.id);
|
||||||
|
expect(url).to.contain('management/kibana/indices');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not notify if isUserAwareOfUnsupportedTimePattern() returns true', async () => {
|
||||||
|
isUserAwareOfUnsupportedTimePattern.returns(true);
|
||||||
|
await createUnsupportedTimePattern();
|
||||||
|
expect(Notifier.prototype._notifs).to.have.length(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,10 +15,6 @@ export function StubIndexPatternsApiClientModule(PrivateProvider) {
|
||||||
class StubIndexPatternsApiClient {
|
class StubIndexPatternsApiClient {
|
||||||
getFieldsForTimePattern = sinon.spy(() => Promise.resolve(nonScriptedFields));
|
getFieldsForTimePattern = sinon.spy(() => Promise.resolve(nonScriptedFields));
|
||||||
getFieldsForWildcard = sinon.spy(() => Promise.resolve(nonScriptedFields));
|
getFieldsForWildcard = sinon.spy(() => Promise.resolve(nonScriptedFields));
|
||||||
testTimePattern = sinon.spy(() => Promise.resolve({
|
|
||||||
all: [],
|
|
||||||
matches: []
|
|
||||||
}))
|
|
||||||
|
|
||||||
swapStubNonScriptedFields = (newNonScriptedFields) => {
|
swapStubNonScriptedFields = (newNonScriptedFields) => {
|
||||||
nonScriptedFields = newNonScriptedFields;
|
nonScriptedFields = newNonScriptedFields;
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
import ngMock from 'ng_mock';
|
||||||
|
import expect from 'expect.js';
|
||||||
|
import Chance from 'chance';
|
||||||
|
|
||||||
|
import { Storage } from 'ui/storage';
|
||||||
|
import StubBrowserStorage from 'test_utils/stub_browser_storage';
|
||||||
|
import StubIndexPatternProvider from 'test_utils/stub_index_pattern';
|
||||||
|
import { IsUserAwareOfUnsupportedTimePatternProvider } from '../unsupported_time_patterns';
|
||||||
|
|
||||||
|
const chance = new Chance();
|
||||||
|
const CONFIG_KEY = 'indexPatterns:warnAboutUnsupportedTimePatterns';
|
||||||
|
|
||||||
|
describe('isUserAwareOfUnsupportedTimePattern()', () => {
|
||||||
|
let setup;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
setup = () => {
|
||||||
|
const store = new StubBrowserStorage();
|
||||||
|
const sessionStorage = new Storage(store);
|
||||||
|
|
||||||
|
// stub some core services
|
||||||
|
ngMock.module('kibana', $provide => {
|
||||||
|
$provide.value('sessionStorage', sessionStorage);
|
||||||
|
});
|
||||||
|
// trigger creation of the injector
|
||||||
|
ngMock.inject();
|
||||||
|
|
||||||
|
const { $injector } = this;
|
||||||
|
const Private = $injector.get('Private');
|
||||||
|
const StubIndexPattern = Private(StubIndexPatternProvider);
|
||||||
|
const isUserAwareOfUnsupportedTimePattern = Private(IsUserAwareOfUnsupportedTimePatternProvider);
|
||||||
|
|
||||||
|
const config = $injector.get('config');
|
||||||
|
config.set(CONFIG_KEY, true); // enable warnings
|
||||||
|
|
||||||
|
return {
|
||||||
|
config,
|
||||||
|
createIndexPattern: () => new StubIndexPattern(chance.word(), null, []),
|
||||||
|
isUserAwareOfUnsupportedTimePattern,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only warns once per index pattern', () => {
|
||||||
|
const {
|
||||||
|
createIndexPattern,
|
||||||
|
isUserAwareOfUnsupportedTimePattern,
|
||||||
|
} = setup();
|
||||||
|
|
||||||
|
const indexPattern1 = createIndexPattern();
|
||||||
|
const indexPattern2 = createIndexPattern();
|
||||||
|
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(indexPattern1)).to.be(false);
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(indexPattern1)).to.be(true);
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(indexPattern2)).to.be(false);
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(indexPattern1)).to.be(true);
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(indexPattern2)).to.be(true);
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(indexPattern1)).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ui config', () => {
|
||||||
|
it('respects setting', () => {
|
||||||
|
const {
|
||||||
|
config,
|
||||||
|
isUserAwareOfUnsupportedTimePattern,
|
||||||
|
createIndexPattern,
|
||||||
|
} = setup();
|
||||||
|
|
||||||
|
config.set(CONFIG_KEY, false);
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(createIndexPattern())).to.be(true);
|
||||||
|
|
||||||
|
config.set(CONFIG_KEY, true);
|
||||||
|
expect(isUserAwareOfUnsupportedTimePattern(createIndexPattern())).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -15,8 +15,19 @@ import { IndexPatternsFlattenHitProvider } from './_flatten_hit';
|
||||||
import { IndexPatternsCalculateIndicesProvider } from './_calculate_indices';
|
import { IndexPatternsCalculateIndicesProvider } from './_calculate_indices';
|
||||||
import { IndexPatternsPatternCacheProvider } from './_pattern_cache';
|
import { IndexPatternsPatternCacheProvider } from './_pattern_cache';
|
||||||
import { FieldsFetcherProvider } from './fields_fetcher_provider';
|
import { FieldsFetcherProvider } from './fields_fetcher_provider';
|
||||||
|
import { IsUserAwareOfUnsupportedTimePatternProvider } from './unsupported_time_patterns';
|
||||||
|
|
||||||
export function IndexPatternProvider(Private, $http, config, kbnIndex, Promise, confirmModalPromise) {
|
export function getRoutes() {
|
||||||
|
return {
|
||||||
|
edit: '/management/kibana/indices/{{id}}',
|
||||||
|
addField: '/management/kibana/indices/{{id}}/create-field',
|
||||||
|
indexedFields: '/management/kibana/indices/{{id}}?_a=(tab:indexedFields)',
|
||||||
|
scriptedFields: '/management/kibana/indices/{{id}}?_a=(tab:scriptedFields)',
|
||||||
|
sourceFilters: '/management/kibana/indices/{{id}}?_a=(tab:sourceFilters)'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IndexPatternProvider(Private, $http, config, kbnIndex, Promise, confirmModalPromise, kbnUrl) {
|
||||||
const fieldformats = Private(RegistryFieldFormatsProvider);
|
const fieldformats = Private(RegistryFieldFormatsProvider);
|
||||||
const getIds = Private(IndexPatternsGetIdsProvider);
|
const getIds = Private(IndexPatternsGetIdsProvider);
|
||||||
const fieldsFetcher = Private(FieldsFetcherProvider);
|
const fieldsFetcher = Private(FieldsFetcherProvider);
|
||||||
|
@ -27,17 +38,12 @@ export function IndexPatternProvider(Private, $http, config, kbnIndex, Promise,
|
||||||
const flattenHit = Private(IndexPatternsFlattenHitProvider);
|
const flattenHit = Private(IndexPatternsFlattenHitProvider);
|
||||||
const calculateIndices = Private(IndexPatternsCalculateIndicesProvider);
|
const calculateIndices = Private(IndexPatternsCalculateIndicesProvider);
|
||||||
const patternCache = Private(IndexPatternsPatternCacheProvider);
|
const patternCache = Private(IndexPatternsPatternCacheProvider);
|
||||||
|
const isUserAwareOfUnsupportedTimePattern = Private(IsUserAwareOfUnsupportedTimePatternProvider);
|
||||||
|
|
||||||
const type = 'index-pattern';
|
const type = 'index-pattern';
|
||||||
const notify = new Notifier();
|
const notify = new Notifier();
|
||||||
const configWatchers = new WeakMap();
|
const configWatchers = new WeakMap();
|
||||||
const docSources = new WeakMap();
|
const docSources = new WeakMap();
|
||||||
const getRoutes = () => ({
|
|
||||||
edit: '/management/kibana/indices/{{id}}',
|
|
||||||
addField: '/management/kibana/indices/{{id}}/create-field',
|
|
||||||
indexedFields: '/management/kibana/indices/{{id}}?_a=(tab:indexedFields)',
|
|
||||||
scriptedFields: '/management/kibana/indices/{{id}}?_a=(tab:scriptedFields)',
|
|
||||||
sourceFilters: '/management/kibana/indices/{{id}}?_a=(tab:sourceFilters)'
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapping = mappingSetup.expandShorthand({
|
const mapping = mappingSetup.expandShorthand({
|
||||||
title: 'text',
|
title: 'text',
|
||||||
|
@ -86,6 +92,18 @@ export function IndexPatternProvider(Private, $http, config, kbnIndex, Promise,
|
||||||
// give index pattern all of the values in _source
|
// give index pattern all of the values in _source
|
||||||
_.assign(indexPattern, response._source);
|
_.assign(indexPattern, response._source);
|
||||||
|
|
||||||
|
if (indexPattern.isUnsupportedTimePattern()) {
|
||||||
|
if (!isUserAwareOfUnsupportedTimePattern(indexPattern)) {
|
||||||
|
const warning = (
|
||||||
|
'Support for time-intervals has been removed. ' +
|
||||||
|
`View the ["${indexPattern.id}" index pattern in management](` +
|
||||||
|
kbnUrl.getRouteHref(indexPattern, 'edit') +
|
||||||
|
') for more information.'
|
||||||
|
);
|
||||||
|
notify.warning(warning, { lifetime: Infinity });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const promise = indexFields(indexPattern);
|
const promise = indexFields(indexPattern);
|
||||||
|
|
||||||
// any time index pattern in ES is updated, update index pattern object
|
// any time index pattern in ES is updated, update index pattern object
|
||||||
|
@ -319,6 +337,10 @@ export function IndexPatternProvider(Private, $http, config, kbnIndex, Promise,
|
||||||
return this.isTimeBased() && !!this.getInterval();
|
return this.isTimeBased() && !!this.getInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isUnsupportedTimePattern() {
|
||||||
|
return !!this.intervalName;
|
||||||
|
}
|
||||||
|
|
||||||
isTimeBasedWildcard() {
|
isTimeBasedWildcard() {
|
||||||
return this.isTimeBased() && this.isWildcard();
|
return this.isTimeBased() && this.isWildcard();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,6 @@ export function createFieldsFetcher(apiClient, config) {
|
||||||
return this.fetchForWildcard(indexPattern.id);
|
return this.fetchForWildcard(indexPattern.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
testTimePattern(indexPatternId) {
|
|
||||||
return apiClient.testTimePattern({
|
|
||||||
pattern: indexPatternId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchForTimePattern(indexPatternId) {
|
fetchForTimePattern(indexPatternId) {
|
||||||
return apiClient.getFieldsForTimePattern({
|
return apiClient.getFieldsForTimePattern({
|
||||||
pattern: indexPatternId,
|
pattern: indexPatternId,
|
||||||
|
|
|
@ -81,20 +81,6 @@ export function createIndexPatternsApiClient($http, basePath) {
|
||||||
request('GET', url).then(resp => resp.fields)
|
request('GET', url).then(resp => resp.fields)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
testTimePattern(options = {}) {
|
|
||||||
const {
|
|
||||||
pattern
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
const url = getUrl(['_test_time_pattern'], {
|
|
||||||
pattern,
|
|
||||||
});
|
|
||||||
|
|
||||||
return notify.event(`testTimePattern(${pattern})`, () => (
|
|
||||||
request('GET', url)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IndexPatternsApiClient();
|
return new IndexPatternsApiClient();
|
||||||
|
|
28
src/ui/public/index_patterns/unsupported_time_patterns.js
Normal file
28
src/ui/public/index_patterns/unsupported_time_patterns.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { BoundToConfigObjProvider } from 'ui/bound_to_config_obj';
|
||||||
|
|
||||||
|
export function IsUserAwareOfUnsupportedTimePatternProvider(Private, $injector) {
|
||||||
|
const BoundToConfigObj = Private(BoundToConfigObjProvider);
|
||||||
|
const sessionStorage = $injector.get('sessionStorage');
|
||||||
|
|
||||||
|
const HISTORY_STORAGE_KEY = 'indexPatterns:warnAboutUnsupportedTimePatterns:history';
|
||||||
|
const FLAGS = new BoundToConfigObj({
|
||||||
|
enabled: '=indexPatterns:warnAboutUnsupportedTimePatterns'
|
||||||
|
});
|
||||||
|
|
||||||
|
return function isUserAwareOfUnsupportedTimePattern(indexPattern) {
|
||||||
|
// The user's disabled the notification. They know about it.
|
||||||
|
if (!FLAGS.enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've already told the user.
|
||||||
|
const previousIds = sessionStorage.get(HISTORY_STORAGE_KEY) || [];
|
||||||
|
if (previousIds.includes(indexPattern.id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's store this for later, so we don't tell the user multiple times.
|
||||||
|
sessionStorage.set(HISTORY_STORAGE_KEY, [...previousIds, indexPattern.id]);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
|
|
||||||
function Storage(store) {
|
export function Storage(store) {
|
||||||
const self = this;
|
const self = this;
|
||||||
self.store = store;
|
self.store = store;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,5 @@ export default function ({ loadTestFile }) {
|
||||||
loadTestFile(require.resolve('./es_errors'));
|
loadTestFile(require.resolve('./es_errors'));
|
||||||
loadTestFile(require.resolve('./fields_for_time_pattern_route'));
|
loadTestFile(require.resolve('./fields_for_time_pattern_route'));
|
||||||
loadTestFile(require.resolve('./fields_for_wildcard_route'));
|
loadTestFile(require.resolve('./fields_for_wildcard_route'));
|
||||||
loadTestFile(require.resolve('./test_time_pattern_route'));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
export default function ({ getService }) {
|
|
||||||
const esArchiver = getService('esArchiver');
|
|
||||||
const supertest = getService('supertest');
|
|
||||||
const chance = getService('chance');
|
|
||||||
|
|
||||||
describe('index_patterns/_test_time_pattern', () => {
|
|
||||||
before(() => esArchiver.load('index_patterns/time_based_indices'));
|
|
||||||
after(() => esArchiver.unload('index_patterns/time_based_indices'));
|
|
||||||
|
|
||||||
it('returns all and matching fields for tested pattern', () =>
|
|
||||||
supertest
|
|
||||||
.get('/api/index_patterns/_test_time_pattern')
|
|
||||||
.query({ pattern: '[yearly-]YYYY' })
|
|
||||||
.expect(200, {
|
|
||||||
all: [
|
|
||||||
'yearly-2018',
|
|
||||||
'yearly-2017',
|
|
||||||
'yearly-2016',
|
|
||||||
],
|
|
||||||
matches: [
|
|
||||||
'yearly-2018',
|
|
||||||
'yearly-2017',
|
|
||||||
'yearly-2016',
|
|
||||||
]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
it('returns all and matching fields for tested pattern', () =>
|
|
||||||
supertest
|
|
||||||
.get('/api/index_patterns/_test_time_pattern')
|
|
||||||
.query({ pattern: '[monthly-]YYYY.MM' })
|
|
||||||
.expect(200, {
|
|
||||||
all: [
|
|
||||||
'monthly-2017.01',
|
|
||||||
'monthly-invalid',
|
|
||||||
],
|
|
||||||
matches: [
|
|
||||||
'monthly-2017.01',
|
|
||||||
]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
it('returns an empty response if it does not match any indexes', () =>
|
|
||||||
supertest
|
|
||||||
.get('/api/index_patterns/_test_time_pattern')
|
|
||||||
.query({ pattern: `[${chance.word({ length: 12 })}]-YYYY` })
|
|
||||||
.expect(200, {
|
|
||||||
all: [],
|
|
||||||
matches: []
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -17,13 +17,6 @@ export default function ({ getService, getPageObjects }) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load with name pattern unchecked', function () {
|
|
||||||
return PageObjects.settings.getTimeBasedIndexPatternCheckbox().isSelected()
|
|
||||||
.then(function (selected) {
|
|
||||||
expect(selected).to.not.be.ok();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should contain default index pattern', function () {
|
it('should contain default index pattern', function () {
|
||||||
const defaultPattern = 'logstash-*';
|
const defaultPattern = 'logstash-*';
|
||||||
|
|
||||||
|
|
|
@ -62,11 +62,6 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
||||||
await PageObjects.common.navigateToApp('settings');
|
await PageObjects.common.navigateToApp('settings');
|
||||||
}
|
}
|
||||||
|
|
||||||
getTimeBasedIndexPatternCheckbox() {
|
|
||||||
// fail faster since we're sometimes checking that it doesn't exist
|
|
||||||
return testSubjects.find('createIndexPatternNameIsPatternCheckBox');
|
|
||||||
}
|
|
||||||
|
|
||||||
getIndexPatternField() {
|
getIndexPatternField() {
|
||||||
return testSubjects.find('createIndexPatternNameInput');
|
return testSubjects.find('createIndexPatternNameInput');
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue