Skip to content
Merged
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 @@ -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[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' }],
Expand All @@ -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 });
Expand Down
41 changes: 3 additions & 38 deletions src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -84,6 +79,7 @@ export class IndexPatternsFetcher {
indexFilter,
fields: options.fields || ['*'],
});

if (type === 'rollup' && rollupIndex) {
const rollupFields: FieldDescriptor[] = [];
const capabilityCheck = getCapabilitiesForRollupIndices(
Expand Down Expand Up @@ -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,
[]
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ describe('ObservabilityDataViews', function () {
timeFieldName: '@timestamp',
title: 'trace-*,apm-*',
name: 'User experience (RUM)',
allowNoIndex: true,
});

expect(dataViews?.createAndSave).toHaveBeenCalledTimes(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion x-pack/test/functional/config.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export default async function ({ readConfigFile }) {
elasticsearch: {
indices: [
{
names: ['rollup-*'],
names: ['rollup-*', 'regular-index*'],
privileges: ['read', 'view_index_metadata'],
},
],
Expand Down