mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[data views] Remove partial index pattern validation (#153350)
## Summary https://github.com/elastic/kibana/pull/151788 established that data views would no longer error when they failed to retrieve a field list. This index pattern validation code exists since previously ALL index pattern segments needed to match in order to avoid an error response from field caps, rather than just one. Now we can remove the validation code and simply pass the index pattern to field caps directly. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: shahzad31 <shahzad31comp@gmail.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
cd96ab465b
commit
29f038071e
7 changed files with 21 additions and 98 deletions
|
@ -334,6 +334,7 @@ export class DataViewEditorService {
|
|||
if (this.type === INDEX_PATTERN_TYPE.ROLLUP) {
|
||||
getFieldsOptions.type = INDEX_PATTERN_TYPE.ROLLUP;
|
||||
getFieldsOptions.rollupIndex = currentState.rollupIndexName || '';
|
||||
getFieldsOptions.allowNoIndex = true;
|
||||
}
|
||||
|
||||
let timestampFieldOptions: TimestampOption[] = [];
|
||||
|
|
|
@ -9,14 +9,10 @@
|
|||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { IndexPatternsFetcher } from '.';
|
||||
import { elasticsearchServiceMock } from '@kbn/core/server/mocks';
|
||||
import * as indexNotFoundException from './index_not_found_exception.json';
|
||||
|
||||
describe('Index Pattern Fetcher - server', () => {
|
||||
let indexPatterns: IndexPatternsFetcher;
|
||||
let esClient: ReturnType<typeof elasticsearchServiceMock.createElasticsearchClient>;
|
||||
const emptyResponse = {
|
||||
indices: [],
|
||||
};
|
||||
const response = {
|
||||
indices: ['b'],
|
||||
fields: [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }],
|
||||
|
@ -27,61 +23,7 @@ describe('Index Pattern Fetcher - server', () => {
|
|||
esClient = elasticsearchServiceMock.createElasticsearchClient();
|
||||
indexPatterns = new IndexPatternsFetcher(esClient);
|
||||
});
|
||||
it('Removes pattern without matching indices', async () => {
|
||||
esClient.fieldCaps
|
||||
.mockResponseOnce(emptyResponse as unknown as estypes.FieldCapsResponse)
|
||||
.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
// first field caps request returns empty
|
||||
const result = await indexPatterns.validatePatternListActive(patternList);
|
||||
expect(result).toEqual(['b', 'c']);
|
||||
});
|
||||
it('Keeps matching and negating patterns', async () => {
|
||||
esClient.fieldCaps
|
||||
.mockResponseOnce(emptyResponse as unknown as estypes.FieldCapsResponse)
|
||||
.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
// first field caps request returns empty
|
||||
const result = await indexPatterns.validatePatternListActive(['-a', 'b', 'c', 'a:-b']);
|
||||
expect(result).toEqual(['-a', 'c', 'a:-b']);
|
||||
});
|
||||
it('Returns all patterns when all match indices', async () => {
|
||||
esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient);
|
||||
const result = await indexPatterns.validatePatternListActive(patternList);
|
||||
expect(result).toEqual(patternList);
|
||||
});
|
||||
it('Removes pattern when error is thrown', async () => {
|
||||
class ServerError extends Error {
|
||||
public body?: Record<string, any>;
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly statusCode: number,
|
||||
errBody?: Record<string, any>
|
||||
) {
|
||||
super(message);
|
||||
this.body = errBody;
|
||||
}
|
||||
}
|
||||
|
||||
esClient.fieldCaps
|
||||
.mockResponseOnce(response as unknown as estypes.FieldCapsResponse)
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.reject(
|
||||
new ServerError('index_not_found_exception', 404, indexNotFoundException)
|
||||
);
|
||||
});
|
||||
|
||||
indexPatterns = new IndexPatternsFetcher(esClient);
|
||||
const result = await indexPatterns.validatePatternListActive(patternList);
|
||||
expect(result).toEqual([patternList[0]]);
|
||||
});
|
||||
it('When allowNoIndices is false, run validatePatternListActive', async () => {
|
||||
esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient);
|
||||
await indexPatterns.getFieldsForWildcard({ pattern: patternList });
|
||||
expect(esClient.fieldCaps).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
it('When allowNoIndices is true, do not run validatePatternListActive', async () => {
|
||||
it('calls fieldcaps once', async () => {
|
||||
esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient, true);
|
||||
await indexPatterns.getFieldsForWildcard({ pattern: patternList });
|
||||
|
|
|
@ -64,18 +64,13 @@ export class IndexPatternsFetcher {
|
|||
fields?: string[];
|
||||
}): Promise<{ fields: FieldDescriptor[]; indices: string[] }> {
|
||||
const { pattern, metaFields = [], fieldCapsOptions, type, rollupIndex, indexFilter } = options;
|
||||
const patternList = Array.isArray(pattern) ? pattern : pattern.split(',');
|
||||
const allowNoIndices = fieldCapsOptions
|
||||
? fieldCapsOptions.allow_no_indices
|
||||
: this.allowNoIndices;
|
||||
let patternListActive: string[] = patternList;
|
||||
// if only one pattern, don't bother with validation. We let getFieldCapabilities fail if the single pattern is bad regardless
|
||||
if (patternList.length > 1 && !allowNoIndices) {
|
||||
patternListActive = await this.validatePatternListActive(patternList);
|
||||
}
|
||||
|
||||
const fieldCapsResponse = await getFieldCapabilities({
|
||||
callCluster: this.elasticsearchClient,
|
||||
indices: patternListActive,
|
||||
indices: pattern,
|
||||
metaFields,
|
||||
fieldCapsOptions: {
|
||||
allow_no_indices: allowNoIndices,
|
||||
|
@ -84,6 +79,7 @@ export class IndexPatternsFetcher {
|
|||
indexFilter,
|
||||
fields: options.fields || ['*'],
|
||||
});
|
||||
|
||||
if (type === 'rollup' && rollupIndex) {
|
||||
const rollupFields: FieldDescriptor[] = [];
|
||||
const capabilityCheck = getCapabilitiesForRollupIndices(
|
||||
|
@ -114,35 +110,4 @@ export class IndexPatternsFetcher {
|
|||
}
|
||||
return fieldCapsResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an index pattern list of only those index pattern strings in the given list that return indices
|
||||
*
|
||||
* @param patternList string[]
|
||||
* @return {Promise<string[]>}
|
||||
*/
|
||||
async validatePatternListActive(patternList: string[]) {
|
||||
const result = await Promise.all(
|
||||
patternList
|
||||
.map(async (index) => {
|
||||
// perserve negated patterns
|
||||
if (index.startsWith('-') || index.includes(':-')) {
|
||||
return true;
|
||||
}
|
||||
const searchResponse = await this.elasticsearchClient.fieldCaps({
|
||||
index,
|
||||
fields: '_id',
|
||||
ignore_unavailable: true,
|
||||
allow_no_indices: false,
|
||||
});
|
||||
return searchResponse.indices.length > 0;
|
||||
})
|
||||
.map((p) => p.catch(() => false))
|
||||
);
|
||||
return result.reduce(
|
||||
(acc: string[], isValid, patternListIndex) =>
|
||||
isValid ? [...acc, patternList[patternListIndex]] : acc,
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,6 +201,14 @@ export default function ({ getService }) {
|
|||
indices: ['basic_index'],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 when neither exists', async () => {
|
||||
await supertest
|
||||
.get('/api/index_patterns/_fields_for_wildcard')
|
||||
.query({ pattern: 'bad_index,bad_index_2' })
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('returns 404 when no patterns exist', async () => {
|
||||
await supertest
|
||||
.get('/api/index_patterns/_fields_for_wildcard')
|
||||
|
|
|
@ -106,6 +106,7 @@ describe('ObservabilityDataViews', function () {
|
|||
timeFieldName: '@timestamp',
|
||||
title: 'trace-*,apm-*',
|
||||
name: 'User experience (RUM)',
|
||||
allowNoIndex: true,
|
||||
});
|
||||
|
||||
expect(dataViews?.createAndSave).toHaveBeenCalledTimes(1);
|
||||
|
|
|
@ -132,11 +132,16 @@ export class ObservabilityDataViews {
|
|||
timeFieldName: '@timestamp',
|
||||
fieldFormats: this.getFieldFormats(app),
|
||||
name: DataTypesLabels[app],
|
||||
allowNoIndex: true,
|
||||
},
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
if (dataView.matchedIndices.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (runtimeFields !== null) {
|
||||
runtimeFields.forEach(({ name, field }) => {
|
||||
dataView.addRuntimeField(name, field);
|
||||
|
@ -162,6 +167,7 @@ export class ObservabilityDataViews {
|
|||
timeFieldName: '@timestamp',
|
||||
fieldFormats: this.getFieldFormats(app),
|
||||
name: DataTypesLabels[app],
|
||||
allowNoIndex: true,
|
||||
});
|
||||
}
|
||||
// we want to make sure field formats remain same
|
||||
|
|
|
@ -461,7 +461,7 @@ export default async function ({ readConfigFile }) {
|
|||
elasticsearch: {
|
||||
indices: [
|
||||
{
|
||||
names: ['rollup-*'],
|
||||
names: ['rollup-*', 'regular-index*'],
|
||||
privileges: ['read', 'view_index_metadata'],
|
||||
},
|
||||
],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue