mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Refactor Index Pattern Creation logic. (#11210)
* Reorder Create Index Pattern logic so local functions are before scope methods. * Extract some Index Pattern Creation logic into sendCreateIndexPatternRequest and getDefaultPatternForInterval services. * Use UI Framework typography components for Index Pattern Creation header. * Separate user-specified variables for configuring the index pattern from controller variables, which just change the UI state. * Rename index to newIndexPattern for clarity. * Use controllerAs and fat arrow functions in Create Index Pattern.
This commit is contained in:
parent
345b64924d
commit
a605b7f478
7 changed files with 402 additions and 356 deletions
|
@ -1,67 +1,80 @@
|
|||
<kbn-management-app section="kibana">
|
||||
<kbn-management-indices>
|
||||
<div ng-controller="managementIndicesCreate" class="kbn-management-indices-create">
|
||||
<div class="page-header">
|
||||
<h1 translate="KIBANA-CONFIGURE_INDEX_PATTERN"></h1>
|
||||
<p translate="KIBANA-MUST_CONFIGURE_INDEX_PATTERN"></p>
|
||||
</div>
|
||||
<div>
|
||||
<form name="form" role="form" class="well" ng-submit="createIndexPattern()">
|
||||
<div
|
||||
ng-controller="managementIndicesCreate as controller"
|
||||
class="kuiViewContent"
|
||||
>
|
||||
<h1
|
||||
class="kuiTitle kuiVerticalRhythm"
|
||||
translate="KIBANA-CONFIGURE_INDEX_PATTERN"
|
||||
></h1>
|
||||
|
||||
<p
|
||||
class="kuiText kuiVerticalRhythm"
|
||||
translate="KIBANA-MUST_CONFIGURE_INDEX_PATTERN"
|
||||
></p>
|
||||
|
||||
<div class="kuiVerticalRhythm">
|
||||
<form name="form" role="form" class="well" ng-submit="controller.createIndexPattern()">
|
||||
<div class="form-group">
|
||||
<label translate="KIBANA-INDEX_NAME_OR_PATTERN"></label>
|
||||
<p class="help-block" ng-if="!index.nameIsPattern" translate="KIBANA-WILDCARD_DYNAMIC_INDEX_PATTERNS"></p>
|
||||
<p class="help-block" ng-if="index.isTimeBased && index.nameIsPattern"><span translate="KIBANA-STATIC_TEXT_IN_DYNAMIC_INDEX_PATTERNS"></span> —
|
||||
<p class="help-block" ng-if="!controller.newIndexPattern.nameIsPattern" translate="KIBANA-WILDCARD_DYNAMIC_INDEX_PATTERNS"></p>
|
||||
<p class="help-block" ng-if="controller.newIndexPattern.isTimeBased && controller.newIndexPattern.nameIsPattern"><span translate="KIBANA-STATIC_TEXT_IN_DYNAMIC_INDEX_PATTERNS"></span> —
|
||||
<small><a target="_blank" href="http://momentjs.com/docs/#/displaying/format/" translate="KIBANA-DATE_FORMAT_DOCS"></a></small></p>
|
||||
<input
|
||||
ng-model="index.name"
|
||||
ng-attr-placeholder="{{index.defaultName}}"
|
||||
data-test-subj="createIndexPatternNameInput"
|
||||
ng-model="controller.newIndexPattern.name"
|
||||
ng-attr-placeholder="{{controller.newIndexPattern.defaultName}}"
|
||||
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 2500, 'blur': 0} }"
|
||||
validate-index-name
|
||||
allow-wildcard
|
||||
name="name"
|
||||
required
|
||||
type="text"
|
||||
class="form-control">
|
||||
<small ng-show="index.nameInterval.name == 'weeks'">
|
||||
<strong translate="KIBANA-NOTE_COLON"></strong>
|
||||
<span translate="KIBANA-WEEKLY_ISO_NOTICE"></span>
|
||||
<span translate="KIBANA-SEE"></span>
|
||||
<a href="https://en.wikipedia.org/wiki/ISO_week_date" target="_blank" title="Wikipedia: ISO Week Date" translate="KIBANA-WIKI_ISO_WEEK_DATE"></a>
|
||||
</small>
|
||||
</div>
|
||||
class="form-control"
|
||||
>
|
||||
<small ng-show="controller.newIndexPattern.nameInterval.name == 'weeks'">
|
||||
<strong translate="KIBANA-NOTE_COLON"></strong>
|
||||
<span translate="KIBANA-WEEKLY_ISO_NOTICE"></span>
|
||||
<span translate="KIBANA-SEE"></span>
|
||||
<a href="https://en.wikipedia.org/wiki/ISO_week_date" target="_blank" title="Wikipedia: ISO Week Date" translate="KIBANA-WIKI_ISO_WEEK_DATE"></a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group time-and-pattern">
|
||||
<label>
|
||||
<input
|
||||
ng-model="index.isTimeBased"
|
||||
data-test-subj="createIndexPatternIsTimeBasedCheckBox"
|
||||
ng-model="controller.newIndexPattern.isTimeBased"
|
||||
type="checkbox">
|
||||
<span translate="KIBANA-CONTAINS_TIME_BASED_EVENTS"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="index.isTimeBased">
|
||||
<div class="form-group" ng-if="controller.newIndexPattern.isTimeBased">
|
||||
<label>
|
||||
<span translate="KIBANA-TIME_FIELD_NAME"></span>
|
||||
|
||||
<kbn-info info="{{ 'KIBANA-FIELD_FILTER_EVENTS_GLOBAL_TIME' | translate }}"></kbn-info>
|
||||
|
||||
<small>
|
||||
<a ng-click="refreshFieldList();" translate="KIBANA-REFRESH_FIELDS"></a>
|
||||
<a ng-click="controller.refreshFieldList();" translate="KIBANA-REFRESH_FIELDS"></a>
|
||||
</small>
|
||||
</label>
|
||||
<select
|
||||
ng-disabled="index.fetchFieldsError"
|
||||
ng-required="!index.fetchFieldsError"
|
||||
ng-options="field.name for field in index.dateFields"
|
||||
ng-model="index.timeField"
|
||||
auto-select-if-only-one="index.dateFields"
|
||||
class="form-control">
|
||||
</select>
|
||||
data-test-subj="createIndexPatternTimeFieldSelect"
|
||||
ng-disabled="controller.fetchFieldsError"
|
||||
ng-required="!controller.fetchFieldsError"
|
||||
ng-options="field.name for field in controller.dateFields"
|
||||
ng-model="controller.newIndexPattern.timeField"
|
||||
auto-select-if-only-one="controller.dateFields"
|
||||
class="form-control"
|
||||
></select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="canExpandIndices()">
|
||||
<div class="form-group" ng-if="controller.canExpandIndices()">
|
||||
<label>
|
||||
<input ng-model="index.expandable" type="checkbox">
|
||||
<input ng-model="controller.newIndexPattern.expandable" type="checkbox">
|
||||
<span translate="KIBANA-EXPAND_INDEX_PATTERN"></span>
|
||||
</label>
|
||||
|
||||
|
@ -79,14 +92,18 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group time-and-pattern">
|
||||
<label ng-if="index.isTimeBased">
|
||||
<input ng-model="index.nameIsPattern" type="checkbox">
|
||||
<label ng-if="controller.newIndexPattern.isTimeBased">
|
||||
<input
|
||||
data-test-subj="createIndexPatternNameIsPatternCheckBox"
|
||||
ng-model="controller.newIndexPattern.nameIsPattern"
|
||||
type="checkbox"
|
||||
>
|
||||
<span translate="KIBANA-INDEX_NAME_CREATED_BY_EVENT_TIMES"></span>
|
||||
<small translate="KIBANA-DEPRECATED"></small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="index.isTimeBased && index.nameIsPattern">
|
||||
<div class="form-group" ng-if="controller.newIndexPattern.isTimeBased && controller.newIndexPattern.nameIsPattern">
|
||||
<div class="alert alert-warning">
|
||||
</p>
|
||||
<h4 translate="KIBANA-ALERT_INDEX_PATTERN_DEPRECATED"></h4>
|
||||
|
@ -102,49 +119,49 @@
|
|||
</label>
|
||||
<select
|
||||
required
|
||||
ng-options="opt.display for opt in index.nameIntervalOptions"
|
||||
ng-model="index.nameInterval"
|
||||
ng-options="opt.display for opt in controller.nameIntervalOptions"
|
||||
ng-model="controller.newIndexPattern.nameInterval"
|
||||
class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<div class="alert alert-danger" ng-repeat="err in index.patternErrors">
|
||||
<div class="alert alert-danger" ng-repeat="err in controller.patternErrors">
|
||||
{{err}}
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" ng-if="index.samples">
|
||||
<div class="alert alert-info" ng-if="controller.samples">
|
||||
<span translate="KIBANA-SAMPLE_ALERT"></span>
|
||||
<ul>
|
||||
<li ng-repeat="sample in index.samples">{{sample}}</li>
|
||||
<li ng-repeat="sample in controller.samples">{{sample}}</li>
|
||||
</ul>
|
||||
<button type="button" ng-click="moreSamples(true)" class="btn btn-default">
|
||||
<button type="button" ng-click="controller.moreSamples(true)" class="btn btn-default">
|
||||
<span translate="KIBANA-EXPAND_SEARCH"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-{{index.existing.class}}" ng-if="index.existing">
|
||||
<span translate="KIBANA-EXISTING_MATCH_PERCENT" translate-values="{ indexExistingMatchPercent: '{{index.existing.matchPercent}}' }"></span>
|
||||
<div class="alert alert-{{controller.existing.class}}" ng-if="controller.existing">
|
||||
<span translate="KIBANA-EXISTING_MATCH_PERCENT" translate-values="{ indexExistingMatchPercent: '{{controller.existing.matchPercent}}' }"></span>
|
||||
<ul>
|
||||
<li ng-repeat="match in index.existing.matches | orderBy:'toString()'| limitTo: index.sampleCount">{{match}}</li>
|
||||
<li ng-repeat="match in controller.existing.matches | orderBy:'toString()'| limitTo: controller.sampleCount">{{match}}</li>
|
||||
</ul>
|
||||
<button
|
||||
ng-if="index.sampleCount < index.existing.matches.length"
|
||||
ng-click="moreSamples()"
|
||||
ng-if="controller.sampleCount < controller.existing.matches.length"
|
||||
ng-click="controller.moreSamples()"
|
||||
type="button"
|
||||
class="btn btn-default">
|
||||
<span translate="KIBANA-EXPAND_SEARCH"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" ng-if="index.existing.failures.length">
|
||||
<div class="alert alert-info" ng-if="controller.existing.failures.length">
|
||||
<span translate="KIBANA-NON_MATCHING_INDICES_AND_ALIASES"></span>
|
||||
<ul>
|
||||
<li ng-repeat="match in index.existing.failures | limitTo: index.sampleCount">{{match}}</li>
|
||||
<li ng-repeat="match in controller.existing.failures | limitTo: controller.sampleCount">{{match}}</li>
|
||||
</ul>
|
||||
<a
|
||||
ng-if="index.sampleCount < index.existing.matches.length"
|
||||
ng-click="moreSamples()"
|
||||
ng-if="controller.sampleCount < controller.existing.matches.length"
|
||||
ng-click="controller.moreSamples()"
|
||||
class="alert-link">
|
||||
<span translate="KIBANA-MORE"></span>
|
||||
</a>
|
||||
|
@ -152,13 +169,14 @@
|
|||
</section>
|
||||
|
||||
<button
|
||||
data-test-subj="submitCreateIndexPatternFromExistingForm"
|
||||
ng-disabled="form.$invalid || index.fetchFieldsError"
|
||||
ng-class="index.fetchFieldsError ? 'btn-default' : 'btn-success'"
|
||||
data-test-subj="createIndexPatternCreateButton"
|
||||
ng-disabled="form.$invalid || controller.fetchFieldsError"
|
||||
ng-class="controller.fetchFieldsError ? 'btn-default' : 'btn-success'"
|
||||
type="submit"
|
||||
class="btn">
|
||||
<span ng-hide="form.name.$error.indexNameInput" ng-if="index.fetchFieldsError">{{index.fetchFieldsError}}</span>
|
||||
<span ng-hide="form.name.$error.indexNameInput" ng-if="!index.fetchFieldsError" translate="KIBANA-CREATE"></span>
|
||||
class="btn"
|
||||
>
|
||||
<span ng-hide="form.name.$error.indexNameInput" ng-if="controller.fetchFieldsError">{{controller.fetchFieldsError}}</span>
|
||||
<span ng-hide="form.name.$error.indexNameInput" ng-if="!controller.fetchFieldsError" translate="KIBANA-CREATE"></span>
|
||||
<span ng-show="form.name.$error.indexNameInput" translate="KIBANA-INVALID_INDEX_PATTERN"></span>
|
||||
</button>
|
||||
</form>
|
||||
|
|
|
@ -6,6 +6,8 @@ import { RefreshKibanaIndex } from '../refresh_kibana_index';
|
|||
import uiRoutes from 'ui/routes';
|
||||
import uiModules from 'ui/modules';
|
||||
import template from './create_index_pattern.html';
|
||||
import { getDefaultPatternForInterval } from './get_default_pattern_for_interval';
|
||||
import { sendCreateIndexPatternRequest } from './send_create_index_pattern_request';
|
||||
|
||||
uiRoutes
|
||||
.when('/management/kibana/index', {
|
||||
|
@ -19,217 +21,50 @@ uiModules.get('apps/management')
|
|||
const intervals = indexPatterns.intervals;
|
||||
let samplePromise;
|
||||
|
||||
// this and child scopes will write pattern vars here
|
||||
const index = $scope.index = {
|
||||
// Configure the new index pattern we're going to create.
|
||||
this.newIndexPattern = {
|
||||
name: config.get('indexPattern:placeholder'),
|
||||
isTimeBased: true,
|
||||
nameIsPattern: false,
|
||||
expandable: false,
|
||||
sampleCount: 5,
|
||||
nameIntervalOptions: intervals,
|
||||
|
||||
fetchFieldsError: $translate.instant('KIBANA-LOADING')
|
||||
nameInterval: _.find(intervals, { name: 'daily' }),
|
||||
timeField: null,
|
||||
};
|
||||
|
||||
index.nameInterval = _.find(index.nameIntervalOptions, { name: 'daily' });
|
||||
index.timeField = null;
|
||||
// UI state.
|
||||
this.dateFields = null;
|
||||
this.sampleCount = 5;
|
||||
this.samples = null;
|
||||
this.existing = null;
|
||||
this.nameIntervalOptions = intervals;
|
||||
this.patternErrors = [];
|
||||
this.fetchFieldsError = $translate.instant('KIBANA-LOADING');
|
||||
|
||||
$scope.canExpandIndices = function () {
|
||||
// to maximize performance in the digest cycle, move from the least
|
||||
// expensive operation to most
|
||||
return index.isTimeBased && !index.nameIsPattern && _.includes(index.name, '*');
|
||||
};
|
||||
|
||||
$scope.refreshFieldList = function () {
|
||||
const timeField = index.timeField;
|
||||
fetchFieldList().then(function (results) {
|
||||
if (timeField) {
|
||||
updateFieldListAndSetTimeField(results, timeField.name);
|
||||
} else {
|
||||
updateFieldList(results);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.createIndexPattern = function () {
|
||||
// get an empty indexPattern to start
|
||||
indexPatterns.get()
|
||||
.then(function (indexPattern) {
|
||||
// set both the id and title to the index index
|
||||
indexPattern.id = indexPattern.title = index.name;
|
||||
if (index.isTimeBased) {
|
||||
indexPattern.timeFieldName = index.timeField.name;
|
||||
if (index.nameIsPattern) {
|
||||
indexPattern.intervalName = index.nameInterval.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!index.expandable && $scope.canExpandIndices()) {
|
||||
indexPattern.notExpandable = true;
|
||||
}
|
||||
|
||||
// fetch the fields
|
||||
return indexPattern.create()
|
||||
.then(function (id) {
|
||||
if (id) {
|
||||
refreshKibanaIndex().then(function () {
|
||||
if (!config.get('defaultIndex')) {
|
||||
config.set('defaultIndex', indexPattern.id);
|
||||
}
|
||||
indexPatterns.cache.clear(indexPattern.id);
|
||||
kbnUrl.change('/management/kibana/indices/' + indexPattern.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// refreshFields calls save() after a successfull fetch, no need to save again
|
||||
// .then(function () { indexPattern.save(); })
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (err instanceof IndexPatternMissingIndices) {
|
||||
notify.error($translate.instant('KIBANA-NO_INDICES_MATCHING_PATTERN'));
|
||||
}
|
||||
else notify.fatal(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.$watchMulti([
|
||||
'index.isTimeBased',
|
||||
'index.nameIsPattern',
|
||||
'index.nameInterval.name'
|
||||
], function (newVal, oldVal) {
|
||||
const isTimeBased = newVal[0];
|
||||
const nameIsPattern = newVal[1];
|
||||
const newDefault = getPatternDefault(newVal[2]);
|
||||
const oldDefault = getPatternDefault(oldVal[2]);
|
||||
|
||||
if (index.name === oldDefault) {
|
||||
index.name = newDefault;
|
||||
}
|
||||
|
||||
if (!isTimeBased) {
|
||||
index.nameIsPattern = false;
|
||||
}
|
||||
|
||||
if (!nameIsPattern) {
|
||||
delete index.nameInterval;
|
||||
delete index.timeField;
|
||||
} else {
|
||||
index.nameInterval = index.nameInterval || intervals.byName.days;
|
||||
index.name = index.name || getPatternDefault(index.nameInterval);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.moreSamples = function (andUpdate) {
|
||||
index.sampleCount += 5;
|
||||
if (andUpdate) updateSamples();
|
||||
};
|
||||
|
||||
$scope.$watchMulti([
|
||||
'index.name',
|
||||
'index.nameInterval'
|
||||
], function (newVal, oldVal) {
|
||||
let lastPromise;
|
||||
resetIndex();
|
||||
samplePromise = lastPromise = updateSamples()
|
||||
.then(function () {
|
||||
promiseMatch(lastPromise, function () {
|
||||
index.samples = null;
|
||||
index.patternErrors = [];
|
||||
});
|
||||
})
|
||||
.catch(function (errors) {
|
||||
promiseMatch(lastPromise, function () {
|
||||
index.existing = null;
|
||||
index.patternErrors = errors;
|
||||
});
|
||||
})
|
||||
.finally(function () {
|
||||
// prevent running when no change happened (ie, first watcher call)
|
||||
if (!_.isEqual(newVal, oldVal)) {
|
||||
fetchFieldList().then(function (results) {
|
||||
if (lastPromise === samplePromise) {
|
||||
updateFieldList(results);
|
||||
samplePromise = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$watchMulti([
|
||||
'index.isTimeBased',
|
||||
'index.sampleCount'
|
||||
], $scope.refreshFieldList);
|
||||
|
||||
function updateSamples() {
|
||||
const patternErrors = [];
|
||||
|
||||
if (!index.nameInterval || !index.name) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const pattern = mockIndexPattern(index);
|
||||
|
||||
return indexPatterns.mapper.getIndicesForIndexPattern(pattern)
|
||||
.catch(function (err) {
|
||||
if (err instanceof IndexPatternMissingIndices) return;
|
||||
notify.error(err);
|
||||
})
|
||||
.then(function (existing) {
|
||||
const all = _.get(existing, 'all', []);
|
||||
const matches = _.get(existing, 'matches', []);
|
||||
if (all.length) {
|
||||
index.existing = {
|
||||
class: 'success',
|
||||
all: all,
|
||||
matches: matches,
|
||||
matchPercent: Math.round((matches.length / all.length) * 100) + '%',
|
||||
failures: _.difference(all, matches)
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
patternErrors.push($translate.instant('KIBANA-PATTERN_DOES_NOT_MATCH_EXIST_INDICES'));
|
||||
const radius = Math.round(index.sampleCount / 2);
|
||||
const samples = intervals.toIndexList(index.name, index.nameInterval, -radius, radius);
|
||||
|
||||
if (_.uniq(samples).length !== samples.length) {
|
||||
patternErrors.push($translate.instant('KIBANA-INVALID_NON_UNIQUE_INDEX_NAME_CREATED'));
|
||||
} else {
|
||||
index.samples = samples;
|
||||
}
|
||||
|
||||
throw patternErrors;
|
||||
});
|
||||
}
|
||||
|
||||
function fetchFieldList() {
|
||||
index.dateFields = index.timeField = index.listUsed = null;
|
||||
const useIndexList = index.isTimeBased && index.nameIsPattern;
|
||||
const fetchFieldList = () => {
|
||||
this.dateFields = this.newIndexPattern.timeField = null;
|
||||
const useIndexList = this.newIndexPattern.isTimeBased && this.newIndexPattern.nameIsPattern;
|
||||
let fetchFieldsError;
|
||||
let dateFields;
|
||||
|
||||
// we don't have enough info to continue
|
||||
if (!index.name) {
|
||||
if (!this.newIndexPattern.name) {
|
||||
fetchFieldsError = $translate.instant('KIBANA-SET_INDEX_NAME_FIRST');
|
||||
return;
|
||||
}
|
||||
|
||||
if (useIndexList && !index.nameInterval) {
|
||||
if (useIndexList && !this.newIndexPattern.nameInterval) {
|
||||
fetchFieldsError = $translate.instant('KIBANA-INTERVAL_INDICES_POPULATED');
|
||||
return;
|
||||
}
|
||||
|
||||
return indexPatterns.mapper.clearCache(index.name)
|
||||
.then(function () {
|
||||
const pattern = mockIndexPattern(index);
|
||||
return indexPatterns.mapper.clearCache(this.newIndexPattern.name)
|
||||
.then(() => {
|
||||
const pattern = mockIndexPattern(this.newIndexPattern);
|
||||
|
||||
return indexPatterns.mapper.getFieldsForIndexPattern(pattern, {
|
||||
skipIndexPatternCache: true,
|
||||
})
|
||||
.catch(function (err) {
|
||||
.catch((err) => {
|
||||
// TODO: we should probably display a message of some kind
|
||||
if (err instanceof IndexPatternMissingIndices) {
|
||||
fetchFieldsError = $translate.instant('KIBANA-INDICES_MATCH_PATTERN');
|
||||
|
@ -239,22 +74,25 @@ uiModules.get('apps/management')
|
|||
throw err;
|
||||
});
|
||||
})
|
||||
.then(function (fields) {
|
||||
.then(fields => {
|
||||
if (fields.length > 0) {
|
||||
fetchFieldsError = null;
|
||||
dateFields = fields.filter(function (field) {
|
||||
return field.type === 'date';
|
||||
});
|
||||
dateFields = fields.filter(field => field.type === 'date');
|
||||
}
|
||||
|
||||
return {
|
||||
fetchFieldsError: fetchFieldsError,
|
||||
dateFields: dateFields
|
||||
fetchFieldsError,
|
||||
dateFields,
|
||||
};
|
||||
}, notify.fatal);
|
||||
}
|
||||
};
|
||||
|
||||
function updateFieldListAndSetTimeField(results, timeFieldName) {
|
||||
const updateFieldList = results => {
|
||||
this.fetchFieldsError = results.fetchFieldsError;
|
||||
this.dateFields = results.dateFields;
|
||||
};
|
||||
|
||||
const updateFieldListAndSetTimeField = (results, timeFieldName) => {
|
||||
updateFieldList(results);
|
||||
|
||||
if (!results.dateFields.length) {
|
||||
|
@ -267,46 +105,15 @@ uiModules.get('apps/management')
|
|||
//assign the field from the results-list
|
||||
//angular recreates a new timefield instance, each time the list is refreshed.
|
||||
//This ensures the selected field matches one of the instances in the list.
|
||||
index.timeField = matchingTimeField ? matchingTimeField : defaultTimeField;
|
||||
}
|
||||
this.newIndexPattern.timeField = matchingTimeField ? matchingTimeField : defaultTimeField;
|
||||
};
|
||||
|
||||
function updateFieldList(results) {
|
||||
index.fetchFieldsError = results.fetchFieldsError;
|
||||
index.dateFields = results.dateFields;
|
||||
}
|
||||
|
||||
function promiseMatch(lastPromise, cb) {
|
||||
if (lastPromise === samplePromise) {
|
||||
cb();
|
||||
} else if (samplePromise != null) {
|
||||
// haven't hit the last promise yet, reset index params
|
||||
resetIndex();
|
||||
}
|
||||
}
|
||||
|
||||
function resetIndex() {
|
||||
index.patternErrors = [];
|
||||
index.samples = null;
|
||||
index.existing = null;
|
||||
index.fetchFieldsError = $translate.instant('KIBANA-LOADING');
|
||||
}
|
||||
|
||||
function getPatternDefault(interval) {
|
||||
switch (interval) {
|
||||
case 'hours':
|
||||
return '[logstash-]YYYY.MM.DD.HH';
|
||||
case 'days':
|
||||
return '[logstash-]YYYY.MM.DD';
|
||||
case 'weeks':
|
||||
return '[logstash-]GGGG.WW';
|
||||
case 'months':
|
||||
return '[logstash-]YYYY.MM';
|
||||
case 'years':
|
||||
return '[logstash-]YYYY';
|
||||
default:
|
||||
return 'logstash-*';
|
||||
}
|
||||
}
|
||||
const resetIndex = () => {
|
||||
this.patternErrors = [];
|
||||
this.samples = null;
|
||||
this.existing = null;
|
||||
this.fetchFieldsError = $translate.instant('KIBANA-LOADING');
|
||||
};
|
||||
|
||||
function mockIndexPattern(index) {
|
||||
// trick the mapper into thinking this is an indexPattern
|
||||
|
@ -315,4 +122,188 @@ uiModules.get('apps/management')
|
|||
intervalName: index.nameInterval
|
||||
};
|
||||
}
|
||||
|
||||
const updateSamples = () => {
|
||||
const patternErrors = [];
|
||||
|
||||
if (!this.newIndexPattern.nameInterval || !this.newIndexPattern.name) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const pattern = mockIndexPattern(this.newIndexPattern);
|
||||
|
||||
return indexPatterns.mapper.getIndicesForIndexPattern(pattern)
|
||||
.catch(err => {
|
||||
if (err instanceof IndexPatternMissingIndices) return;
|
||||
notify.error(err);
|
||||
})
|
||||
.then(existing => {
|
||||
const all = _.get(existing, 'all', []);
|
||||
const matches = _.get(existing, 'matches', []);
|
||||
|
||||
if (all.length) {
|
||||
return this.existing = {
|
||||
class: 'success',
|
||||
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.newIndexPattern.name, this.newIndexPattern.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;
|
||||
});
|
||||
};
|
||||
|
||||
this.canExpandIndices = () => {
|
||||
// to maximize performance in the digest cycle, move from the least
|
||||
// expensive operation to most
|
||||
return this.newIndexPattern.isTimeBased && !this.newIndexPattern.nameIsPattern && _.includes(this.newIndexPattern.name, '*');
|
||||
};
|
||||
|
||||
this.refreshFieldList = () => {
|
||||
const timeField = this.newIndexPattern.timeField;
|
||||
fetchFieldList().then(results => {
|
||||
if (timeField) {
|
||||
updateFieldListAndSetTimeField(results, timeField.name);
|
||||
} else {
|
||||
updateFieldList(results);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.createIndexPattern = () => {
|
||||
const id = this.newIndexPattern.name;
|
||||
const timeFieldName =
|
||||
this.newIndexPattern.isTimeBased
|
||||
? this.newIndexPattern.timeField.name
|
||||
: undefined;
|
||||
|
||||
// Only event-time-based index patterns set an intervalName.
|
||||
const intervalName =
|
||||
this.newIndexPattern.isTimeBased && this.newIndexPattern.nameIsPattern
|
||||
? this.newIndexPattern.nameInterval.name
|
||||
: undefined;
|
||||
|
||||
const notExpandable =
|
||||
!this.newIndexPattern.expandable && this.canExpandIndices()
|
||||
? true
|
||||
: undefined;
|
||||
|
||||
sendCreateIndexPatternRequest(indexPatterns, {
|
||||
id,
|
||||
timeFieldName,
|
||||
intervalName,
|
||||
notExpandable,
|
||||
}).then(createdId => {
|
||||
if (!createdId) {
|
||||
return;
|
||||
}
|
||||
|
||||
refreshKibanaIndex().then(() => {
|
||||
if (!config.get('defaultIndex')) {
|
||||
config.set('defaultIndex', id);
|
||||
}
|
||||
|
||||
indexPatterns.cache.clear(id);
|
||||
kbnUrl.change(`/management/kibana/indices/${id}`);
|
||||
});
|
||||
}).catch(err => {
|
||||
if (err instanceof IndexPatternMissingIndices) {
|
||||
return notify.error($translate.instant('KIBANA-NO_INDICES_MATCHING_PATTERN'));
|
||||
}
|
||||
|
||||
notify.fatal(err);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watchMulti([
|
||||
'controller.newIndexPattern.isTimeBased',
|
||||
'controller.newIndexPattern.nameIsPattern',
|
||||
'controller.newIndexPattern.nameInterval.name'
|
||||
], (newVal, oldVal) => {
|
||||
const isTimeBased = newVal[0];
|
||||
const nameIsPattern = newVal[1];
|
||||
const newDefault = getDefaultPatternForInterval(newVal[2]);
|
||||
const oldDefault = getDefaultPatternForInterval(oldVal[2]);
|
||||
|
||||
if (this.newIndexPattern.name === oldDefault) {
|
||||
this.newIndexPattern.name = newDefault;
|
||||
}
|
||||
|
||||
if (!isTimeBased) {
|
||||
this.newIndexPattern.nameIsPattern = false;
|
||||
}
|
||||
|
||||
if (!nameIsPattern) {
|
||||
delete this.newIndexPattern.nameInterval;
|
||||
delete this.newIndexPattern.timeField;
|
||||
} else {
|
||||
this.newIndexPattern.nameInterval = this.newIndexPattern.nameInterval || intervals.byName.days;
|
||||
this.newIndexPattern.name = this.newIndexPattern.name || getDefaultPatternForInterval(this.newIndexPattern.nameInterval);
|
||||
}
|
||||
});
|
||||
|
||||
this.moreSamples = andUpdate => {
|
||||
this.sampleCount += 5;
|
||||
if (andUpdate) updateSamples();
|
||||
};
|
||||
|
||||
$scope.$watchMulti([
|
||||
'controller.newIndexPattern.name',
|
||||
'controller.newIndexPattern.nameInterval'
|
||||
], (newVal, oldVal) => {
|
||||
function promiseMatch(lastPromise, cb) {
|
||||
if (lastPromise === samplePromise) {
|
||||
cb();
|
||||
} else if (samplePromise != null) {
|
||||
// haven't hit the last promise yet, reset index params
|
||||
resetIndex();
|
||||
}
|
||||
}
|
||||
|
||||
let lastPromise;
|
||||
resetIndex();
|
||||
samplePromise = lastPromise = updateSamples()
|
||||
.then(() => {
|
||||
promiseMatch(lastPromise, () => {
|
||||
this.samples = null;
|
||||
this.patternErrors = [];
|
||||
});
|
||||
})
|
||||
.catch(errors => {
|
||||
promiseMatch(lastPromise, () => {
|
||||
this.existing = null;
|
||||
this.patternErrors = errors;
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
// prevent running when no change happened (ie, first watcher call)
|
||||
if (!_.isEqual(newVal, oldVal)) {
|
||||
fetchFieldList().then(results => {
|
||||
if (lastPromise === samplePromise) {
|
||||
updateFieldList(results);
|
||||
samplePromise = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$watchMulti([
|
||||
'controller.newIndexPattern.isTimeBased',
|
||||
'controller.sampleCount'
|
||||
], () => {
|
||||
this.refreshFieldList();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
const intervalToDefaultPatternMap = {
|
||||
hours: '[logstash-]YYYY.MM.DD.HH',
|
||||
days: '[logstash-]YYYY.MM.DD',
|
||||
weeks: '[logstash-]GGGG.WW',
|
||||
months: '[logstash-]YYYY.MM',
|
||||
years: '[logstash-]YYYY',
|
||||
};
|
||||
|
||||
export function getDefaultPatternForInterval(interval) {
|
||||
const defaultPattern = intervalToDefaultPatternMap[interval];
|
||||
|
||||
if (defaultPattern) {
|
||||
return defaultPattern;
|
||||
}
|
||||
|
||||
return 'logstash-*';
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
export function sendCreateIndexPatternRequest(indexPatterns, {
|
||||
id,
|
||||
timeFieldName,
|
||||
intervalName,
|
||||
notExpandable,
|
||||
}) {
|
||||
// get an empty indexPattern to start
|
||||
return indexPatterns.get()
|
||||
.then(indexPattern => {
|
||||
// set both the id and title to the same value
|
||||
indexPattern.id = indexPattern.title = id;
|
||||
|
||||
Object.assign(indexPattern, {
|
||||
timeFieldName,
|
||||
intervalName,
|
||||
notExpandable,
|
||||
});
|
||||
|
||||
// fetch the fields
|
||||
return indexPattern.create();
|
||||
});
|
||||
}
|
|
@ -25,8 +25,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
})
|
||||
// try to find the checkbox (this shouldn fail)
|
||||
.then(function () {
|
||||
const waitTime = 10000;
|
||||
return PageObjects.settings.getTimeBasedIndexPatternCheckbox(waitTime);
|
||||
return PageObjects.settings.getTimeBasedIndexPatternCheckbox();
|
||||
})
|
||||
.then(function () {
|
||||
PageObjects.common.saveScreenshot('Settings-indices-hide-time-based-index-pattern');
|
||||
|
|
|
@ -50,7 +50,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
});
|
||||
|
||||
it('should not be enable creation', function () {
|
||||
return PageObjects.settings.getCreateButton().isEnabled()
|
||||
return PageObjects.settings.getCreateIndexPatternButton().isEnabled()
|
||||
.then(function (enabled) {
|
||||
expect(enabled).to.not.be.ok();
|
||||
});
|
||||
|
|
|
@ -32,18 +32,18 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
|
||||
getAdvancedSettings(propertyName) {
|
||||
log.debug('in setAdvancedSettings');
|
||||
return testSubjects.find('advancedSetting-' + propertyName + '-currentValue')
|
||||
return testSubjects.find(`advancedSetting-${propertyName}-currentValue`)
|
||||
.getVisibleText();
|
||||
}
|
||||
|
||||
async setAdvancedSettings(propertyName, propertyValue) {
|
||||
await testSubjects.click('advancedSetting-' + propertyName + '-editButton');
|
||||
await testSubjects.click(`advancedSetting-${propertyName}-editButton`);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.common.sleep(1000);
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('option[label="' + propertyValue + '"]').click();
|
||||
.findByCssSelector(`option[label="${propertyValue}"]`).click();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await testSubjects.click('advancedSetting-' + propertyName + '-saveButton');
|
||||
await testSubjects.click(`advancedSetting-${propertyName}-saveButton`);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
|
@ -52,25 +52,20 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
}
|
||||
|
||||
getTimeBasedEventsCheckbox() {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('input[ng-model="index.isTimeBased"]');
|
||||
return testSubjects.find('createIndexPatternIsTimeBasedCheckBox');
|
||||
}
|
||||
|
||||
getTimeBasedIndexPatternCheckbox(timeout) {
|
||||
timeout = timeout || defaultFindTimeout;
|
||||
getTimeBasedIndexPatternCheckbox() {
|
||||
// fail faster since we're sometimes checking that it doesn't exist
|
||||
return remote.setFindTimeout(timeout)
|
||||
.findByCssSelector('input[ng-model="index.nameIsPattern"]');
|
||||
return testSubjects.find('createIndexPatternNameIsPatternCheckBox');
|
||||
}
|
||||
|
||||
getIndexPatternField() {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('[ng-model="index.name"]');
|
||||
return testSubjects.find('createIndexPatternNameInput');
|
||||
}
|
||||
|
||||
getTimeFieldNameField() {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findDisplayedByCssSelector('select[ng-model="index.timeField"]');
|
||||
return testSubjects.find('createIndexPatternTimeFieldSelect');
|
||||
}
|
||||
|
||||
async selectTimeFieldOption(selection) {
|
||||
|
@ -91,6 +86,10 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
.findDisplayedByCssSelector('option[label="' + selection + '"]');
|
||||
}
|
||||
|
||||
getCreateIndexPatternButton() {
|
||||
return testSubjects.find('createIndexPatternCreateButton');
|
||||
}
|
||||
|
||||
getCreateButton() {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findDisplayedByCssSelector('[type="submit"]');
|
||||
|
@ -111,12 +110,12 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
|
||||
getConfigureHeader() {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('h1');
|
||||
.findByCssSelector('h1');
|
||||
}
|
||||
|
||||
getTableHeader() {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findAllByCssSelector('table.table.table-condensed thead tr th');
|
||||
.findAllByCssSelector('table.table.table-condensed thead tr th');
|
||||
}
|
||||
|
||||
sortBy(columnName) {
|
||||
|
@ -142,11 +141,11 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
|
||||
getTableRow(rowNumber, colNumber) {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
// passing in zero-based index, but adding 1 for css 1-based indexes
|
||||
.findByCssSelector('div.agg-table-paginated table.table.table-condensed tbody tr:nth-child(' +
|
||||
(rowNumber + 1) + ') td.ng-scope:nth-child(' +
|
||||
(colNumber + 1) + ') span.ng-binding'
|
||||
);
|
||||
// passing in zero-based index, but adding 1 for css 1-based indexes
|
||||
.findByCssSelector('div.agg-table-paginated table.table.table-condensed tbody tr:nth-child(' +
|
||||
(rowNumber + 1) + ') td.ng-scope:nth-child(' +
|
||||
(colNumber + 1) + ') span.ng-binding'
|
||||
);
|
||||
}
|
||||
|
||||
getFieldsTabCount() {
|
||||
|
@ -172,27 +171,27 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
getPageSize() {
|
||||
let selectedItemLabel = '';
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findAllByCssSelector('select.ng-pristine.ng-valid.ng-untouched option')
|
||||
.then(function (chartTypes) {
|
||||
function getChartType(chart) {
|
||||
const thisChart = chart;
|
||||
return chart.isSelected()
|
||||
.then(function (isSelected) {
|
||||
if (isSelected === true) {
|
||||
return thisChart.getProperty('label')
|
||||
.then(function (theLabel) {
|
||||
selectedItemLabel = theLabel;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
.findAllByCssSelector('select.ng-pristine.ng-valid.ng-untouched option')
|
||||
.then(function (chartTypes) {
|
||||
function getChartType(chart) {
|
||||
const thisChart = chart;
|
||||
return chart.isSelected()
|
||||
.then(function (isSelected) {
|
||||
if (isSelected === true) {
|
||||
return thisChart.getProperty('label')
|
||||
.then(function (theLabel) {
|
||||
selectedItemLabel = theLabel;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getChartTypesPromises = chartTypes.map(getChartType);
|
||||
return Promise.all(getChartTypesPromises);
|
||||
})
|
||||
.then(() => {
|
||||
return selectedItemLabel;
|
||||
});
|
||||
const getChartTypesPromises = chartTypes.map(getChartType);
|
||||
return Promise.all(getChartTypesPromises);
|
||||
})
|
||||
.then(() => {
|
||||
return selectedItemLabel;
|
||||
});
|
||||
}
|
||||
|
||||
async getFieldNames() {
|
||||
|
@ -218,68 +217,68 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
|
||||
async setFieldTypeFilter(type) {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('select[data-test-subj="indexedFieldTypeFilterDropdown"] > option[label="' + type + '"]')
|
||||
.click();
|
||||
.findByCssSelector('select[data-test-subj="indexedFieldTypeFilterDropdown"] > option[label="' + type + '"]')
|
||||
.click();
|
||||
}
|
||||
|
||||
async setScriptedFieldLanguageFilter(language) {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('select[data-test-subj="scriptedFieldLanguageFilterDropdown"] > option[label="' + language + '"]')
|
||||
.click();
|
||||
.findByCssSelector('select[data-test-subj="scriptedFieldLanguageFilterDropdown"] > option[label="' + language + '"]')
|
||||
.click();
|
||||
}
|
||||
|
||||
async goToPage(pageNum) {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('ul.pagination-other-pages-list.pagination-sm.ng-scope li.ng-scope:nth-child(' +
|
||||
(pageNum + 1) + ') a.ng-binding')
|
||||
.click();
|
||||
.findByCssSelector('ul.pagination-other-pages-list.pagination-sm.ng-scope li.ng-scope:nth-child(' +
|
||||
(pageNum + 1) + ') a.ng-binding')
|
||||
.click();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async openControlsRow(row) {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('table.table.table-condensed tbody tr:nth-child(' +
|
||||
(row + 1) + ') td.ng-scope div.actions a.btn.btn-xs.btn-default i.fa.fa-pencil')
|
||||
.click();
|
||||
.findByCssSelector('table.table.table-condensed tbody tr:nth-child(' +
|
||||
(row + 1) + ') td.ng-scope div.actions a.btn.btn-xs.btn-default i.fa.fa-pencil')
|
||||
.click();
|
||||
}
|
||||
|
||||
async openControlsByName(name) {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('div.actions a.btn.btn-xs.btn-default[href$="/' + name + '"]')
|
||||
.click();
|
||||
.findByCssSelector('div.actions a.btn.btn-xs.btn-default[href$="/' + name + '"]')
|
||||
.click();
|
||||
}
|
||||
|
||||
async increasePopularity() {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('button.btn.btn-default[aria-label="Plus"]')
|
||||
.click();
|
||||
.findByCssSelector('button.btn.btn-default[aria-label="Plus"]')
|
||||
.click();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
getPopularity() {
|
||||
return remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('input[ng-model="editor.field.count"]')
|
||||
.getProperty('value');
|
||||
.findByCssSelector('input[ng-model="editor.field.count"]')
|
||||
.getProperty('value');
|
||||
}
|
||||
|
||||
async controlChangeCancel() {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('button.btn.btn-primary[aria-label="Cancel"]')
|
||||
.click();
|
||||
.findByCssSelector('button.btn.btn-primary[aria-label="Cancel"]')
|
||||
.click();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async controlChangeSave() {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('button.btn.btn-success.ng-binding[aria-label="Update Field"]')
|
||||
.click();
|
||||
.findByCssSelector('button.btn.btn-success.ng-binding[aria-label="Update Field"]')
|
||||
.click();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async setPageSize(size) {
|
||||
await remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector(`[data-test-subj="paginateControlsPageSizeSelect"] option[label="${size}"]`)
|
||||
.click();
|
||||
.findByCssSelector(`[data-test-subj="paginateControlsPageSizeSelect"] option[label="${size}"]`)
|
||||
.click();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue