mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Backport 5c7c39aaf3
(#17051)
This commit is contained in:
parent
7f5de0f99a
commit
076a0b2745
8 changed files with 225 additions and 113 deletions
|
@ -0,0 +1,13 @@
|
|||
# Create Index Pattern
|
||||
|
||||
This is meant to serve as a guide to this area of code.
|
||||
|
||||
## Bye bye regressions
|
||||
In order to prevent future regressions, there are a few scenarios
|
||||
that need to be tested with each change to this area of the code.
|
||||
|
||||
- Cross cluster search
|
||||
- Ensure changes work properly in a CCS environment
|
||||
- A solid CCS environment involves various indices on all nodes including the controlling node.
|
||||
- Alias support
|
||||
- Indices are the most common use case, but we also support aliases.
|
|
@ -36,7 +36,7 @@ exports[`StepIndexPattern should render normally 1`] = `
|
|||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={true}
|
||||
isNextStepDisabled={false}
|
||||
onQueryChanged={[Function]}
|
||||
query="k"
|
||||
/>
|
||||
|
@ -54,14 +54,19 @@ exports[`StepIndexPattern should render normally 1`] = `
|
|||
"name": "es",
|
||||
},
|
||||
],
|
||||
"exactMatchedIndices": Array [],
|
||||
"partialMatchedIndices": Array [],
|
||||
"visibleIndices": Array [
|
||||
"exactMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"partialMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "es",
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"visibleIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -77,9 +82,6 @@ exports[`StepIndexPattern should render normally 1`] = `
|
|||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
|
@ -120,11 +122,7 @@ exports[`StepIndexPattern should render some indices 1`] = `
|
|||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"partialMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"partialMatchedIndices": Array [],
|
||||
"visibleIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
|
@ -174,7 +172,7 @@ exports[`StepIndexPattern should render the loading state 1`] = `
|
|||
</EuiPanel>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should show errors 1`] = `
|
||||
exports[`StepIndexPattern should search for partial indices for queries not ending in a wildcard 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
|
@ -182,16 +180,12 @@ exports[`StepIndexPattern should show errors 1`] = `
|
|||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={
|
||||
Array [
|
||||
"Your input contains invalid characters or spaces. Please omit: \\\\, /, ?, \\", <, >, |",
|
||||
]
|
||||
}
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={true}
|
||||
isNextStepDisabled={true}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={false}
|
||||
onQueryChanged={[Function]}
|
||||
query="?"
|
||||
query="k"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
|
@ -224,7 +218,7 @@ exports[`StepIndexPattern should show errors 1`] = `
|
|||
],
|
||||
}
|
||||
}
|
||||
query="?"
|
||||
query="k"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
|
@ -240,3 +234,32 @@ exports[`StepIndexPattern should show errors 1`] = `
|
|||
/>
|
||||
</EuiPanel>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should show errors 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={
|
||||
Array [
|
||||
"Your input contains invalid characters or spaces. Please omit: \\\\, /, ?, \\", <, >, |",
|
||||
]
|
||||
}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={true}
|
||||
isNextStepDisabled={true}
|
||||
onQueryChanged={[Function]}
|
||||
query="?"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LoadingIndices />
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
</EuiPanel>
|
||||
`;
|
||||
|
|
|
@ -15,14 +15,13 @@ jest.mock('../../../lib/get_indices', () => ({
|
|||
];
|
||||
},
|
||||
}));
|
||||
jest.mock('../../../lib/is_query_a_match', () => ({ isQueryAMatch: () => true }));
|
||||
|
||||
const allIndices = [{ name: 'kibana' }, { name: 'es' }];
|
||||
const esService = {};
|
||||
const goToNextStep = () => {};
|
||||
|
||||
describe('StepIndexPattern', () => {
|
||||
it('should render normally', () => {
|
||||
it('should render normally', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
|
@ -33,6 +32,11 @@ describe('StepIndexPattern', () => {
|
|||
/>
|
||||
);
|
||||
|
||||
// Ensure all promises resolve
|
||||
await new Promise(resolve => process.nextTick(resolve));
|
||||
// Ensure the state changes are reflected
|
||||
component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -64,11 +68,10 @@ describe('StepIndexPattern', () => {
|
|||
const instance = component.instance();
|
||||
|
||||
await instance.onQueryChanged({
|
||||
nativeEvent: { data: 'k' },
|
||||
target: { value: 'k' }
|
||||
});
|
||||
|
||||
component.update();
|
||||
await component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
@ -86,7 +89,6 @@ describe('StepIndexPattern', () => {
|
|||
const instance = component.instance();
|
||||
|
||||
await instance.onQueryChanged({
|
||||
nativeEvent: { data: '?' },
|
||||
target: { value: '?' }
|
||||
});
|
||||
|
||||
|
@ -101,11 +103,6 @@ describe('StepIndexPattern', () => {
|
|||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={{
|
||||
find: () => ({ savedObjects: [
|
||||
{ attributes: { title: 'k*' } }
|
||||
] })
|
||||
}}
|
||||
goToNextStep={goToNextStep}
|
||||
/>
|
||||
);
|
||||
|
@ -117,4 +114,23 @@ describe('StepIndexPattern', () => {
|
|||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should search for partial indices for queries not ending in a wildcard', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
goToNextStep={goToNextStep}
|
||||
initialQuery="k"
|
||||
/>
|
||||
);
|
||||
|
||||
// Ensure all promises resolve
|
||||
await new Promise(resolve => process.nextTick(resolve));
|
||||
// Ensure the state changes are reflected
|
||||
component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,6 +35,7 @@ export class StepIndexPattern extends Component {
|
|||
super(props);
|
||||
this.state = {
|
||||
partialMatchedIndices: [],
|
||||
exactMatchedIndices: [],
|
||||
isLoadingIndices: false,
|
||||
query: props.initialQuery,
|
||||
appendedWildcard: false,
|
||||
|
@ -42,16 +43,32 @@ export class StepIndexPattern extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
if (this.state.query) {
|
||||
this.fetchIndices(this.state.query);
|
||||
}
|
||||
}
|
||||
|
||||
fetchIndices = async (query) => {
|
||||
const { esService } = this.props;
|
||||
|
||||
this.setState({ isLoadingIndices: true });
|
||||
const esQuery = query.endsWith('*') ? query : `${query}*`;
|
||||
const partialMatchedIndices = await getIndices(esService, esQuery, MAX_SEARCH_SIZE);
|
||||
createReasonableWait(() => this.setState({ partialMatchedIndices, isLoadingIndices: false }));
|
||||
this.setState({ isLoadingIndices: true, indexPatternExists: false });
|
||||
if (query.endsWith('*')) {
|
||||
const exactMatchedIndices = await getIndices(esService, query, MAX_SEARCH_SIZE);
|
||||
createReasonableWait(() => this.setState({ exactMatchedIndices, isLoadingIndices: false }));
|
||||
}
|
||||
else {
|
||||
const partialMatchedIndices = await getIndices(esService, `${query}*`, MAX_SEARCH_SIZE);
|
||||
const exactMatchedIndices = await getIndices(esService, query, MAX_SEARCH_SIZE);
|
||||
createReasonableWait(() => this.setState({
|
||||
partialMatchedIndices,
|
||||
exactMatchedIndices,
|
||||
isLoadingIndices: false
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
onQueryChanged = (e) => {
|
||||
onQueryChanged = e => {
|
||||
const { appendedWildcard } = this.state;
|
||||
const { target } = e;
|
||||
|
||||
|
@ -148,9 +165,15 @@ export class StepIndexPattern extends Component {
|
|||
|
||||
render() {
|
||||
const { isIncludingSystemIndices, allIndices } = this.props;
|
||||
const { query, partialMatchedIndices } = this.state;
|
||||
const { query, partialMatchedIndices, exactMatchedIndices } = this.state;
|
||||
|
||||
const matchedIndices = getMatchedIndices(allIndices, partialMatchedIndices, query, isIncludingSystemIndices);
|
||||
const matchedIndices = getMatchedIndices(
|
||||
allIndices,
|
||||
partialMatchedIndices,
|
||||
exactMatchedIndices,
|
||||
query,
|
||||
isIncludingSystemIndices
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiPanel paddingSize="l">
|
||||
|
|
|
@ -13,84 +13,95 @@ const indices = [
|
|||
{ name: '.kibana' }
|
||||
];
|
||||
|
||||
const partialIndices = [
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
{ name: '.kibana' },
|
||||
];
|
||||
|
||||
const exactIndices = [
|
||||
{ name: 'kibana' },
|
||||
{ name: '.kibana' },
|
||||
];
|
||||
|
||||
describe('getMatchedIndices', () => {
|
||||
describe('allIndices', () => {
|
||||
it('should return all indices', () => {
|
||||
const query = 'ki';
|
||||
const { allIndices } = getMatchedIndices(indices, indices, query, true);
|
||||
expect(allIndices).toEqual(indices);
|
||||
});
|
||||
it('should return all indices', () => {
|
||||
const {
|
||||
allIndices,
|
||||
exactMatchedIndices,
|
||||
partialMatchedIndices,
|
||||
visibleIndices,
|
||||
} = getMatchedIndices(indices, partialIndices, exactIndices, '*', true);
|
||||
|
||||
it('should return all indices except for system indices', () => {
|
||||
const query = 'ki';
|
||||
const { allIndices } = getMatchedIndices(indices, indices, query, false);
|
||||
expect(allIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
{ name: 'logstash' },
|
||||
{ name: 'packetbeat' },
|
||||
{ name: 'metricbeat' },
|
||||
]);
|
||||
});
|
||||
expect(allIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
{ name: 'logstash' },
|
||||
{ name: 'packetbeat' },
|
||||
{ name: 'metricbeat' },
|
||||
{ name: '.kibana' },
|
||||
]);
|
||||
|
||||
expect(exactMatchedIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: '.kibana' },
|
||||
]);
|
||||
|
||||
expect(partialMatchedIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
{ name: '.kibana' },
|
||||
]);
|
||||
|
||||
expect(visibleIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: '.kibana' },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('exactMatchedIndices', () => {
|
||||
it('should return all exact matched indices', () => {
|
||||
const query = 'ki*';
|
||||
const { exactMatchedIndices } = getMatchedIndices(indices, indices, query, true);
|
||||
expect(exactMatchedIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: '.kibana' },
|
||||
]);
|
||||
});
|
||||
it('should return all indices except for system indices', () => {
|
||||
const {
|
||||
allIndices,
|
||||
exactMatchedIndices,
|
||||
partialMatchedIndices,
|
||||
visibleIndices,
|
||||
} = getMatchedIndices(indices, partialIndices, exactIndices, '*', false);
|
||||
|
||||
it('should return all exact matched indices except for system indices', () => {
|
||||
const query = 'ki*';
|
||||
const { exactMatchedIndices } = getMatchedIndices(indices, indices, query, false);
|
||||
expect(exactMatchedIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
]);
|
||||
});
|
||||
expect(allIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
{ name: 'logstash' },
|
||||
{ name: 'packetbeat' },
|
||||
{ name: 'metricbeat' },
|
||||
]);
|
||||
|
||||
expect(exactMatchedIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
]);
|
||||
|
||||
expect(partialMatchedIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
]);
|
||||
|
||||
expect(visibleIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('partialMatchedIndices', () => {
|
||||
it('should return all partial matched indices', () => {
|
||||
const query = 'ki*';
|
||||
const partialIndices = indices.slice(1);
|
||||
const { partialMatchedIndices } = getMatchedIndices(indices, partialIndices, query, true);
|
||||
expect(partialMatchedIndices).toEqual(partialIndices);
|
||||
});
|
||||
it('should return partial matches as visible if there are no exact', () => {
|
||||
const { visibleIndices } = getMatchedIndices(indices, partialIndices, [], '*', true);
|
||||
|
||||
it('should return all partial matched indices except for system indices', () => {
|
||||
const query = 'ki*';
|
||||
const partialIndices = indices.slice(1);
|
||||
const { partialMatchedIndices } = getMatchedIndices(indices, partialIndices, query, false);
|
||||
expect(partialMatchedIndices).toEqual([
|
||||
{ name: 'es' },
|
||||
{ name: 'logstash' },
|
||||
{ name: 'packetbeat' },
|
||||
{ name: 'metricbeat' },
|
||||
]);
|
||||
});
|
||||
expect(visibleIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
{ name: '.kibana' },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('visibleIndices', () => {
|
||||
it('should return all visible indices', () => {
|
||||
const query = 'foo*';
|
||||
const { visibleIndices } = getMatchedIndices(indices, indices, query, true);
|
||||
expect(visibleIndices).toEqual(indices);
|
||||
});
|
||||
it('should return all indices as visible if there are no exact or partial', () => {
|
||||
const { visibleIndices } = getMatchedIndices(indices, [], [], '*', true);
|
||||
|
||||
it('should return all visible indices except for system indices', () => {
|
||||
const query = 'foo*';
|
||||
const { visibleIndices } = getMatchedIndices(indices, indices, query, false);
|
||||
expect(visibleIndices).toEqual([
|
||||
{ name: 'kibana' },
|
||||
{ name: 'es' },
|
||||
{ name: 'logstash' },
|
||||
{ name: 'packetbeat' },
|
||||
{ name: 'metricbeat' },
|
||||
]);
|
||||
});
|
||||
expect(visibleIndices).toEqual(indices);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export function createReasonableWait(cb) {
|
||||
return setTimeout(cb, 500);
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,11 @@ export async function getIndices(es, rawPattern, limit) {
|
|||
return [];
|
||||
}
|
||||
|
||||
// This should never match anything so do not bother
|
||||
if (pattern === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// We need to always provide a limit and not rely on the default
|
||||
if (!limit) {
|
||||
throw '`getIndices()` was called without the required `limit` parameter.';
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { MAX_NUMBER_OF_MATCHING_INDICES } from '../constants';
|
||||
import { isQueryAMatch } from './is_query_a_match';
|
||||
|
||||
function filterSystemIndices(indices, isIncludingSystemIndices) {
|
||||
if (!indices) {
|
||||
|
@ -14,18 +13,40 @@ function filterSystemIndices(indices, isIncludingSystemIndices) {
|
|||
return acceptableIndices.slice(0, MAX_NUMBER_OF_MATCHING_INDICES);
|
||||
}
|
||||
|
||||
/**
|
||||
This utility is designed to do a couple of things:
|
||||
|
||||
1) Take in list of indices and filter out system indices if necessary
|
||||
2) Return a `visible` list based on a priority order.
|
||||
|
||||
We are passing in three separate lists because they each represent
|
||||
something slightly different.
|
||||
|
||||
- `unfilteredAllIndices`
|
||||
This is the result of the initial `*` query and represents all known indices
|
||||
- `unfilteredPartialMatchedIndices`
|
||||
This is the result of searching against the query with an added `*`. This is only
|
||||
used when the query does not end in an `*` and represents potential matches in the UI
|
||||
- `unfilteredExactMatchedIndices
|
||||
This is the result of searching against a query that already ends in `*`.
|
||||
We call this `exact` matches because ES is telling us exactly what it matches
|
||||
*/
|
||||
export function getMatchedIndices(
|
||||
unfilteredAllIndices,
|
||||
unfilteredPartialMatchedIndices,
|
||||
unfilteredExactMatchedIndices,
|
||||
query,
|
||||
isIncludingSystemIndices
|
||||
) {
|
||||
const allIndices = filterSystemIndices(unfilteredAllIndices, isIncludingSystemIndices);
|
||||
const partialMatchedIndices = filterSystemIndices(unfilteredPartialMatchedIndices, isIncludingSystemIndices);
|
||||
const exactMatchedIndices = filterSystemIndices(unfilteredExactMatchedIndices, isIncludingSystemIndices);
|
||||
|
||||
const exactIndices = partialMatchedIndices.filter(({ name }) => isQueryAMatch(query, name));
|
||||
const exactMatchedIndices = filterSystemIndices(exactIndices, isIncludingSystemIndices);
|
||||
|
||||
// We need to pick one to show in the UI and there is a priority here
|
||||
// 1) If there are exact matches, show those as the query is good to go
|
||||
// 2) If there are no exact matches, but there are partial matches,
|
||||
// show the partial matches
|
||||
// 3) If there are no exact or partial matches, just show all indices
|
||||
let visibleIndices;
|
||||
if (exactMatchedIndices.length) {
|
||||
visibleIndices = exactMatchedIndices;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue