[data view mgmt] only use resolve api for getting index list (#127085)

* only use resolve api for getting index list
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Matthew Kime 2022-03-14 13:11:17 -05:00 committed by GitHub
parent 719ccb6d87
commit 9739c5fa15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 13 additions and 192 deletions

View file

@ -143,14 +143,13 @@ const IndexPatternEditorFlyoutContentComponent = ({
isRollupIndex: () => false,
pattern: '*',
showAllIndices: allowHidden,
searchClient,
}).then((dataSources) => {
setAllSources(dataSources);
const matchedSet = getMatchedIndices(dataSources, [], [], allowHidden);
setMatchedIndices(matchedSet);
setIsLoadingSources(false);
});
}, [http, allowHidden, searchClient]);
}, [http, allowHidden]);
// loading list of index patterns
useEffect(() => {
@ -407,7 +406,6 @@ const loadMatchedIndices = memoizeOne(
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
searchClient,
});
indexRequests.push(exactMatchedQuery);
// provide default value when not making a request for the partialMatchQuery
@ -418,14 +416,12 @@ const loadMatchedIndices = memoizeOne(
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
searchClient,
});
const partialMatchQuery = getIndices({
http,
isRollupIndex,
pattern: `${query}*`,
showAllIndices: allowHidden,
searchClient,
});
indexRequests.push(exactMatchQuery);

View file

@ -66,7 +66,6 @@ export const EmptyPrompts: FC<Props> = ({ allSources, onCancel, children, loadSo
isRollupIndex: () => false,
pattern: '*:*',
showAllIndices: false,
searchClient,
}).then((dataSources) => {
setRemoteClustersExist(!!dataSources.filter(removeAliases).length);
});

View file

@ -6,15 +6,9 @@
* Side Public License, v 1.
*/
import {
getIndices,
getIndicesViaSearch,
responseToItemArray,
dedupeMatchedItems,
} from './get_indices';
import { getIndices, responseToItemArray } from './get_indices';
import { httpServiceMock } from '../../../../core/public/mocks';
import { ResolveIndexResponseItemIndexAttrs, MatchedItem } from '../types';
import { Observable } from 'rxjs';
import { ResolveIndexResponseItemIndexAttrs } from '../types';
export const successfulResolveResponse = {
indices: [
@ -38,41 +32,8 @@ export const successfulResolveResponse = {
],
};
const successfulSearchResponse = {
isPartial: false,
isRunning: false,
rawResponse: {
aggregations: {
indices: {
buckets: [{ key: 'kibana_sample_data_ecommerce' }, { key: '.kibana_1' }],
},
},
},
};
const partialSearchResponse = {
isPartial: true,
isRunning: true,
rawResponse: {
hits: {
total: 2,
hits: [],
},
},
};
const errorSearchResponse = {
isPartial: true,
isRunning: false,
};
const isRollupIndex = () => false;
const getTags = () => [];
const searchClient = () =>
new Observable((observer) => {
observer.next(successfulSearchResponse);
observer.complete();
}) as any;
const http = httpServiceMock.createStartContract();
http.get.mockResolvedValue(successfulResolveResponse);
@ -83,7 +44,6 @@ describe('getIndices', () => {
const result = await getIndices({
http,
pattern: 'kibana',
searchClient: uncalledSearchClient,
isRollupIndex,
});
expect(http.get).toHaveBeenCalled();
@ -95,42 +55,23 @@ describe('getIndices', () => {
it('should make two calls in cross cluser case', async () => {
http.get.mockResolvedValue(successfulResolveResponse);
const result = await getIndices({ http, pattern: '*:kibana', searchClient, isRollupIndex });
const result = await getIndices({ http, pattern: '*:kibana', isRollupIndex });
expect(http.get).toHaveBeenCalled();
expect(result.length).toBe(4);
expect(result.length).toBe(3);
expect(result[0].name).toBe('f-alias');
expect(result[1].name).toBe('foo');
expect(result[2].name).toBe('kibana_sample_data_ecommerce');
expect(result[3].name).toBe('remoteCluster1:bar-01');
expect(result[2].name).toBe('remoteCluster1:bar-01');
});
it('should ignore ccs query-all', async () => {
expect((await getIndices({ http, pattern: '*:', searchClient, isRollupIndex })).length).toBe(0);
expect((await getIndices({ http, pattern: '*:', isRollupIndex })).length).toBe(0);
});
it('should ignore a single comma', async () => {
expect((await getIndices({ http, pattern: ',', searchClient, isRollupIndex })).length).toBe(0);
expect((await getIndices({ http, pattern: ',*', searchClient, isRollupIndex })).length).toBe(0);
expect(
(await getIndices({ http, pattern: ',foobar', searchClient, isRollupIndex })).length
).toBe(0);
});
it('should work with partial responses', async () => {
const searchClientPartialResponse = () =>
new Observable((observer) => {
observer.next(partialSearchResponse);
observer.next(successfulSearchResponse);
observer.complete();
}) as any;
const result = await getIndices({
http,
pattern: '*:kibana',
searchClient: searchClientPartialResponse,
isRollupIndex,
});
expect(result.length).toBe(4);
expect((await getIndices({ http, pattern: ',', isRollupIndex })).length).toBe(0);
expect((await getIndices({ http, pattern: ',*', isRollupIndex })).length).toBe(0);
expect((await getIndices({ http, pattern: ',foobar', isRollupIndex })).length).toBe(0);
});
it('response object to item array', () => {
@ -162,33 +103,12 @@ describe('getIndices', () => {
expect(responseToItemArray({}, getTags)).toEqual([]);
});
it('matched items are deduped', () => {
const setA = [{ name: 'a' }, { name: 'b' }] as MatchedItem[];
const setB = [{ name: 'b' }, { name: 'c' }] as MatchedItem[];
expect(dedupeMatchedItems(setA, setB)).toHaveLength(3);
});
describe('errors', () => {
it('should handle thrown errors gracefully', async () => {
http.get.mockImplementationOnce(() => {
throw new Error('Test error');
});
const result = await getIndices({ http, pattern: 'kibana', searchClient, isRollupIndex });
expect(result.length).toBe(0);
});
it('getIndicesViaSearch should handle error responses gracefully', async () => {
const searchClientErrorResponse = () =>
new Observable((observer) => {
observer.next(errorSearchResponse);
observer.complete();
}) as any;
const result = await getIndicesViaSearch({
pattern: '*:kibana',
searchClient: searchClientErrorResponse,
showAllIndices: false,
isRollupIndex,
});
const result = await getIndices({ http, pattern: 'kibana', isRollupIndex });
expect(result.length).toBe(0);
});
});

View file

@ -8,18 +8,11 @@
import { sortBy } from 'lodash';
import { HttpStart } from 'kibana/public';
import { map, filter } from 'rxjs/operators';
import { i18n } from '@kbn/i18n';
import { Tag, INDEX_PATTERN_TYPE } from '../types';
import { MatchedItem, ResolveIndexResponse, ResolveIndexResponseItemIndexAttrs } from '../types';
import { MAX_SEARCH_SIZE } from '../constants';
import {
DataPublicPluginStart,
IEsSearchResponse,
isErrorResponse,
isCompleteResponse,
} from '../../../data/public';
import { IEsSearchResponse } from '../../../data/public';
const aliasLabel = i18n.translate('indexPatternEditor.aliasLabel', { defaultMessage: 'Alias' });
const dataStreamLabel = i18n.translate('indexPatternEditor.dataStreamLabel', {
@ -78,42 +71,6 @@ export const searchResponseToArray =
}
};
export const getIndicesViaSearch = async ({
pattern,
searchClient,
showAllIndices,
isRollupIndex,
}: {
pattern: string;
searchClient: DataPublicPluginStart['search']['search'];
showAllIndices: boolean;
isRollupIndex: (indexName: string) => boolean;
}): Promise<MatchedItem[]> =>
searchClient({
params: {
ignoreUnavailable: true,
expand_wildcards: showAllIndices ? 'all' : 'open',
index: pattern,
body: {
size: 0, // no hits
aggs: {
indices: {
terms: {
field: '_index',
size: MAX_SEARCH_SIZE,
},
},
},
},
},
})
.pipe(
filter((resp) => isCompleteResponse(resp) || isErrorResponse(resp)),
map(searchResponseToArray(getIndexTags(isRollupIndex), showAllIndices))
)
.toPromise()
.catch(() => []);
export const getIndicesViaResolve = async ({
http,
pattern,
@ -137,48 +94,18 @@ export const getIndicesViaResolve = async ({
}
});
/**
* Takes two MatchedItem[]s and returns a merged set, with the second set prrioritized over the first based on name
*
* @param matchedA
* @param matchedB
*/
export const dedupeMatchedItems = (matchedA: MatchedItem[], matchedB: MatchedItem[]) => {
const mergedMatchedItems = matchedA.reduce((col, item) => {
col[item.name] = item;
return col;
}, {} as Record<string, MatchedItem>);
matchedB.reduce((col, item) => {
col[item.name] = item;
return col;
}, mergedMatchedItems);
return Object.values(mergedMatchedItems).sort((a, b) => {
if (a.name > b.name) return 1;
if (b.name > a.name) return -1;
return 0;
});
};
export async function getIndices({
http,
pattern: rawPattern = '',
showAllIndices = false,
searchClient,
isRollupIndex,
}: {
http: HttpStart;
pattern: string;
showAllIndices?: boolean;
searchClient: DataPublicPluginStart['search']['search'];
isRollupIndex: (indexName: string) => boolean;
}): Promise<MatchedItem[]> {
const pattern = rawPattern.trim();
const isCCS = pattern.indexOf(':') !== -1;
const requests: Array<Promise<MatchedItem[]>> = [];
// Searching for `*:` fails for CCS environments. The search request
// is worthless anyways as the we should only send a request
@ -198,33 +125,12 @@ export async function getIndices({
return [];
}
const promiseResolve = getIndicesViaResolve({
return getIndicesViaResolve({
http,
pattern,
showAllIndices,
isRollupIndex,
}).catch(() => []);
requests.push(promiseResolve);
if (isCCS) {
// CCS supports ±1 major version. We won't be able to expect resolve endpoint to exist until v9
const promiseSearch = getIndicesViaSearch({
pattern,
searchClient,
showAllIndices,
isRollupIndex,
}).catch(() => []);
requests.push(promiseSearch);
}
const responses = await Promise.all(requests);
if (responses.length === 2) {
const [resolveResponse, searchResponse] = responses;
return dedupeMatchedItems(searchResponse, resolveResponse);
} else {
return responses[0];
}
}
export const responseToItemArray = (