Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[WIP] Surface information about indices and index patterns when creating a new index pattern. #12325

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,17 @@
translate="KIBANA-INDEX_NAME_OR_PATTERN"
></label>

<div class="kuiVerticalRhythm kuiVerticalRhythmSmall">
<input
class="kuiTextInput kuiTextInput--large"
data-test-subj="createIndexPatternNameInput"
ng-model="controller.formValues.name"
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"
validate-index-name
allow-wildcard
name="name"
required
type="text"
>
<div class="kuiVerticalRhythmSmall kuiVerticalRhythm">
<index-pattern-combo-box
index-pattern="controller.formValues.name"
set-index-pattern="controller.setIndexPattern"
matching-indices="controller.matchingIndices"
matching-template-index-patterns="controller.matchingTemplateIndexPatterns"
partial-matching-indices="controller.partialMatchingIndices"
partial-matching-template-index-patterns="controller.partialMatchingTemplateIndexPatterns"
all-indices="controller.allIndices"
all-template-index-patterns="controller.allTemplateIndexPatterns"
></index-pattern-combo-box>
</div>

<!-- Input error text -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,28 @@ import { uiModules } from 'ui/modules';
import template from './create_index_pattern.html';
import { sendCreateIndexPatternRequest } from './send_create_index_pattern_request';
import { pickCreateButtonText } from './pick_create_button_text';
import './index_pattern_combo_box_directive';
import 'ui/indices';

uiRoutes
.when('/management/kibana/index', {
template,
});

uiModules.get('apps/management')
.controller('managementIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise, $translate) {
.controller('managementIndicesCreate', function (
$injector,
$scope,
$translate,
config,
es,
indexPatterns,
kbnUrl,
Notifier,
Private,
Promise
) {
const indicesService = $injector.get('indices');
const notify = new Notifier();
const refreshKibanaIndex = Private(RefreshKibanaIndex);
let loadingCount = 0;
Expand Down Expand Up @@ -111,6 +125,86 @@ uiModules.get('apps/management')
return nonFieldOptions[0];
};

this.isDropdownVisible = false;
this.matchingIndices = [];
this.matchingTemplateIndexPatterns = [];
this.partialMatchingIndices = [];
this.partialMatchingTemplateIndexPatterns = [];
this.allIndices = [];
this.allTemplateIndexPatterns = [];

/**
* This powers the `refresh` within ui-select which is called
* as a way to async fetch results based on the typed in query
*
* @param {string} searchQuery
*/
this.fetchMatches = () => {
// Default to searching for all indices.
const exactSearchQuery = this.formValues.name;
let partialSearchQuery = this.formValues.name;

if (!_.endsWith(partialSearchQuery, '*')) {
partialSearchQuery = `${partialSearchQuery}*`;
}
if (!_.startsWith(partialSearchQuery, '*')) {
partialSearchQuery = `*${partialSearchQuery}`;
}

Promise.all([
indicesService.getIndices(exactSearchQuery),
indicesService.getTemplateIndexPatterns(exactSearchQuery),
indicesService.getIndices(partialSearchQuery),
indicesService.getTemplateIndexPatterns(partialSearchQuery),
indicesService.getIndices('*'),
indicesService.getTemplateIndexPatterns('*'),
])
.then(([
matchingIndices,
matchingTemplateIndexPatterns,
partialMatchingIndices,
partialMatchingTemplateIndexPatterns,
allIndices,
allTemplateIndexPatterns,
]) => {
this.matchingIndices = matchingIndices.sort();
this.matchingTemplateIndexPatterns = matchingTemplateIndexPatterns.sort();
this.partialMatchingIndices = partialMatchingIndices.sort();
this.partialMatchingTemplateIndexPatterns = partialMatchingTemplateIndexPatterns.sort();
this.allIndices = allIndices.sort();
this.allTemplateIndexPatterns = allTemplateIndexPatterns.sort();
});
};

this.setIndexPattern = indexPattern => {
this.formValues.name = indexPattern;
};

this.hasInput = () => {
return Boolean(this.formValues.name);
};

this.hasNoSimilarMatches = () => {
return (
!this.matchingIndices.length
&& !this.matchingTemplateIndexPatterns.length
&& !this.partialMatchingIndices.length
&& !this.partialMatchingTemplateIndexPatterns.length
);
};

this.hasSimilarMatches = () => {
if (!this.matchingIndices.length && !this.matchingTemplateIndexPatterns.length) {
return this.partialMatchingIndices.length || this.partialMatchingTemplateIndexPatterns.length;
}

return false;
};

this.hasExactMatches = () => {
return this.matchingIndices.length;
};

this.isTimeBased = () => {
if (!this.formValues.timeFieldOption) {
// if they haven't choosen a time field, assume they will
Expand Down Expand Up @@ -241,6 +335,7 @@ uiModules.get('apps/management')

$scope.$watch('controller.formValues.name', () => {
this.refreshTimeFieldOptions();
this.fetchMatches();
});

$scope.$watchMulti([
Expand All @@ -251,4 +346,6 @@ uiModules.get('apps/management')
const state = { loading, invalidIndexName, timeFieldOption };
this.createButtonText = pickCreateButtonText($translate, state);
});

this.fetchMatches();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import React, {
Component,
} from 'react';
import PropTypes from 'prop-types';

import {
KuiComboBox,
KuiComboBoxOption,
KuiComboBoxOptions,
KuiComboBoxSection,
KuiComboBoxText,
KuiComboBoxTitle,
} from 'ui_framework/components';

export class IndexPatternComboBox extends Component {
constructor() {
super();

this.hasInput = this.hasInput.bind(this);
this.hasNoSimilarMatches = this.hasNoSimilarMatches.bind(this);
this.hasPartialMatches = this.hasPartialMatches.bind(this);
this.hasExactMatches = this.hasExactMatches.bind(this);
this.onInputChange = this.onInputChange.bind(this);
this.onOptionClick = this.onOptionClick.bind(this);
this.renderOption = this.renderOption.bind(this);
this.renderNoMatches = this.renderNoMatches.bind(this);
this.renderPartialMatches = this.renderPartialMatches.bind(this);
this.renderExactMatches = this.renderExactMatches.bind(this);
}

hasInput() {
return Boolean(this.props.indexPattern);
}

hasNoSimilarMatches() {
return (
!this.props.matchingIndices.length
&& !this.props.matchingTemplateIndexPatterns.length
&& !this.props.partialMatchingIndices.length
&& !this.props.partialMatchingTemplateIndexPatterns.length
);
}

hasPartialMatches() {
if (!this.props.matchingIndices.length && !this.props.matchingTemplateIndexPatterns.length) {
return (
this.props.partialMatchingIndices.length
|| this.props.partialMatchingTemplateIndexPatterns.length
);
}

return false;
}

hasExactMatches() {
return Boolean(this.props.matchingIndices.length);
}

onInputChange(e) {
this.props.setIndexPattern(e.target.value);
}

onOptionClick(option) {
this.props.setIndexPattern(option);
}

renderOption(index) {
return (
<KuiComboBoxOption
key={index}
onClick={() => this.props.setIndexPattern(index)}
>
{index}
</KuiComboBoxOption>
);
}

renderNoMatches() {
let noMatchesIntro;

if (this.hasInput()) {
noMatchesIntro = (
<span>Sorry, there are no indices or template index
patterns which match or look similar to this index pattern.</span>
);
}

const allIndices = this.props.allIndices.map(this.renderOption);
const allTemplateIndexPatterns = this.props.allTemplateIndexPatterns.map(this.renderOption);

return [(
<KuiComboBoxSection key={0}>
<KuiComboBoxText>
{noMatchesIntro} These are all of the indices and template index patterns
that are available to you.
</KuiComboBoxText>
</KuiComboBoxSection>
), (
<KuiComboBoxSection key={1}>
<KuiComboBoxTitle>
All indices ({this.props.allIndices.length})
</KuiComboBoxTitle>

<KuiComboBoxOptions>
{allIndices}
</KuiComboBoxOptions>
</KuiComboBoxSection>
), (
<KuiComboBoxSection key={2}>
<KuiComboBoxTitle>
All template index patterns ({this.props.allTemplateIndexPatterns.length})
</KuiComboBoxTitle>

<KuiComboBoxOptions>
{allTemplateIndexPatterns}
</KuiComboBoxOptions>
</KuiComboBoxSection>
)];
}

renderPartialMatches() {
const partialMatchingIndices = this.props.partialMatchingIndices.map(this.renderOption);
const partialMatchingTemplateIndexPatterns = this.props.partialMatchingTemplateIndexPatterns.map(this.renderOption);

return [(
<KuiComboBoxSection key={0}>
<KuiComboBoxText>
No indices match this pattern but there
are <strong>{this.props.partialMatchingIndices.length}</strong> indices
and <strong>{this.props.partialMatchingTemplateIndexPatterns.length}</strong> template index patterns
with similar names.
</KuiComboBoxText>
</KuiComboBoxSection>
), (
<KuiComboBoxSection key={1}>
<KuiComboBoxTitle>
Similar indices ({this.props.partialMatchingIndices.length})
</KuiComboBoxTitle>

<KuiComboBoxOptions>
{partialMatchingIndices}
</KuiComboBoxOptions>
</KuiComboBoxSection>
), (
<KuiComboBoxSection key={2}>
<KuiComboBoxTitle>
Similar template index patterns ({this.props.partialMatchingTemplateIndexPatterns.length})
</KuiComboBoxTitle>

<KuiComboBoxOptions>
{partialMatchingTemplateIndexPatterns}
</KuiComboBoxOptions>
</KuiComboBoxSection>
)];
}

renderExactMatches() {
const matchingIndices = this.props.matchingIndices.map(this.renderOption);

return [(
<KuiComboBoxSection key={0}>
<KuiComboBoxText>
<strong>{this.props.partialMatchingIndices.length}</strong> indices match this pattern.
</KuiComboBoxText>
</KuiComboBoxSection>
), (
<KuiComboBoxSection key={1}>
<KuiComboBoxTitle>
Matching indices ({this.props.partialMatchingIndices.length})
</KuiComboBoxTitle>

<KuiComboBoxOptions>
{matchingIndices}
</KuiComboBoxOptions>
</KuiComboBoxSection>
)];
}

render() {
let matches;

if (!this.hasInput()) {
matches = this.renderNoMatches();
} else if (this.hasExactMatches()) {
matches = this.renderExactMatches();
} else if (this.hasPartialMatches()) {
matches = this.renderPartialMatches();
} else {
matches = this.renderNoMatches();
}

return (
<KuiComboBox
value={this.props.indexPattern}
onChange={this.onInputChange}
size="large"
>
{matches}
</KuiComboBox>
);
}
}

IndexPatternComboBox.propTypes = {
indexPattern: PropTypes.string,
setIndexPattern: PropTypes.func,
matchingIndices: PropTypes.array,
matchingTemplateIndexPatterns: PropTypes.array,
partialMatchingIndices: PropTypes.array,
partialMatchingTemplateIndexPatterns: PropTypes.array,
allIndices: PropTypes.array,
allTemplateIndexPatterns: PropTypes.array,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'ngreact';

import {
IndexPatternComboBox,
} from './index_pattern_combo_box';

import { uiModules } from 'ui/modules';

const app = uiModules.get('app/kibana', ['react']);

app.directive('indexPatternComboBox', function (reactDirective) {
return reactDirective(IndexPatternComboBox);
});
Loading