diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsfetcher.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsfetcher.md
index 3ba3c862bf16a..608d738676bcf 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsfetcher.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsfetcher.md
@@ -22,4 +22,5 @@ export declare class IndexPatternsFetcher
| --- | --- | --- |
| [getFieldsForTimePattern(options)](./kibana-plugin-plugins-data-server.indexpatternsfetcher.getfieldsfortimepattern.md) | | Get a list of field objects for a time pattern |
| [getFieldsForWildcard(options)](./kibana-plugin-plugins-data-server.indexpatternsfetcher.getfieldsforwildcard.md) | | Get a list of field objects for an index pattern that may contain wildcards |
+| [validatePatternListActive(patternList)](./kibana-plugin-plugins-data-server.indexpatternsfetcher.validatepatternlistactive.md) | | Returns an index pattern list of only those index pattern strings in the given list that return indices |
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsfetcher.validatepatternlistactive.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsfetcher.validatepatternlistactive.md
new file mode 100644
index 0000000000000..8944c41204323
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsfetcher.validatepatternlistactive.md
@@ -0,0 +1,24 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternsFetcher](./kibana-plugin-plugins-data-server.indexpatternsfetcher.md) > [validatePatternListActive](./kibana-plugin-plugins-data-server.indexpatternsfetcher.validatepatternlistactive.md)
+
+## IndexPatternsFetcher.validatePatternListActive() method
+
+Returns an index pattern list of only those index pattern strings in the given list that return indices
+
+Signature:
+
+```typescript
+validatePatternListActive(patternList: string[]): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| patternList | string[]
| |
+
+Returns:
+
+`Promise`
+
diff --git a/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.test.ts b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.test.ts
new file mode 100644
index 0000000000000..ffdd47e5cdf49
--- /dev/null
+++ b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.test.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { IndexPatternsFetcher } from '.';
+import { ElasticsearchClient } from 'kibana/server';
+import * as indexNotFoundException from '../../../common/search/test_data/index_not_found_exception.json';
+
+describe('Index Pattern Fetcher - server', () => {
+ let indexPatterns: IndexPatternsFetcher;
+ let esClient: ElasticsearchClient;
+ const emptyResponse = {
+ body: {
+ count: 0,
+ },
+ };
+ const response = {
+ body: {
+ count: 1115,
+ },
+ };
+ const patternList = ['a', 'b', 'c'];
+ beforeEach(() => {
+ esClient = ({
+ count: jest.fn().mockResolvedValueOnce(emptyResponse).mockResolvedValue(response),
+ } as unknown) as ElasticsearchClient;
+ indexPatterns = new IndexPatternsFetcher(esClient);
+ });
+
+ it('Removes pattern without matching indices', async () => {
+ const result = await indexPatterns.validatePatternListActive(patternList);
+ expect(result).toEqual(['b', 'c']);
+ });
+
+ it('Returns all patterns when all match indices', async () => {
+ esClient = ({
+ count: jest.fn().mockResolvedValue(response),
+ } as unknown) as ElasticsearchClient;
+ indexPatterns = new IndexPatternsFetcher(esClient);
+ const result = await indexPatterns.validatePatternListActive(patternList);
+ expect(result).toEqual(patternList);
+ });
+ it('Removes pattern when "index_not_found_exception" error is thrown', async () => {
+ class ServerError extends Error {
+ public body?: Record;
+ constructor(
+ message: string,
+ public readonly statusCode: number,
+ errBody?: Record
+ ) {
+ super(message);
+ this.body = errBody;
+ }
+ }
+
+ esClient = ({
+ count: jest
+ .fn()
+ .mockResolvedValueOnce(response)
+ .mockRejectedValue(
+ new ServerError('index_not_found_exception', 404, indexNotFoundException)
+ ),
+ } as unknown) as ElasticsearchClient;
+ indexPatterns = new IndexPatternsFetcher(esClient);
+ const result = await indexPatterns.validatePatternListActive(patternList);
+ expect(result).toEqual([patternList[0]]);
+ });
+});
diff --git a/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts
index cc8bfe28bbc9a..3acdde33f599e 100644
--- a/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts
+++ b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts
@@ -58,9 +58,16 @@ export class IndexPatternsFetcher {
rollupIndex?: string;
}): Promise {
const { pattern, metaFields, fieldCapsOptions, type, rollupIndex } = options;
+ const patternList = Array.isArray(pattern) ? pattern : pattern.split(',');
+ 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) {
+ patternListActive = await this.validatePatternListActive(patternList);
+ }
const fieldCapsResponse = await getFieldCapabilities(
this.elasticsearchClient,
- pattern,
+ // if none of the patterns are active, pass the original list to get an error
+ patternListActive.length > 0 ? patternListActive : patternList,
metaFields,
{
allow_no_indices: fieldCapsOptions
@@ -68,6 +75,7 @@ export class IndexPatternsFetcher {
: this.allowNoIndices,
}
);
+
if (type === 'rollup' && rollupIndex) {
const rollupFields: FieldDescriptor[] = [];
const rollupIndexCapabilities = getCapabilitiesForRollupIndices(
@@ -118,4 +126,34 @@ export class IndexPatternsFetcher {
}
return await getFieldCapabilities(this.elasticsearchClient, indices, metaFields);
}
+
+ /**
+ * Returns an index pattern list of only those index pattern strings in the given list that return indices
+ *
+ * @param patternList string[]
+ * @return {Promise}
+ */
+ async validatePatternListActive(patternList: string[]) {
+ const result = await Promise.all(
+ patternList
+ .map((pattern) =>
+ this.elasticsearchClient.count({
+ index: pattern,
+ })
+ )
+ .map((p) =>
+ p.catch((e) => {
+ if (e.body.error.type === 'index_not_found_exception') {
+ return { body: { count: 0 } };
+ }
+ throw e;
+ })
+ )
+ );
+ return result.reduce(
+ (acc: string[], { body: { count } }, patternListIndex) =>
+ count > 0 ? [...acc, patternList[patternListIndex]] : acc,
+ []
+ );
+ }
}
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 68582a9d877e9..3b1440f211bfe 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -885,6 +885,7 @@ export class IndexPatternsFetcher {
type?: string;
rollupIndex?: string;
}): Promise;
+ validatePatternListActive(patternList: string[]): Promise;
}
// Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStart" needs to be exported by the entry point index.d.ts
diff --git a/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js b/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js
index e84052e58dac4..87c5aa535ccd9 100644
--- a/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js
+++ b/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js
@@ -17,6 +17,55 @@ export default function ({ getService }) {
expect(resp.body.fields).to.eql(sortBy(resp.body.fields, 'name'));
};
+ const testFields = [
+ {
+ type: 'boolean',
+ esTypes: ['boolean'],
+ searchable: true,
+ aggregatable: true,
+ name: 'bar',
+ readFromDocValues: true,
+ },
+ {
+ type: 'string',
+ esTypes: ['text'],
+ searchable: true,
+ aggregatable: false,
+ name: 'baz',
+ readFromDocValues: false,
+ },
+ {
+ type: 'string',
+ esTypes: ['keyword'],
+ searchable: true,
+ aggregatable: true,
+ name: 'baz.keyword',
+ readFromDocValues: true,
+ subType: { multi: { parent: 'baz' } },
+ },
+ {
+ type: 'number',
+ esTypes: ['long'],
+ searchable: true,
+ aggregatable: true,
+ name: 'foo',
+ readFromDocValues: true,
+ },
+ {
+ aggregatable: true,
+ esTypes: ['keyword'],
+ name: 'nestedField.child',
+ readFromDocValues: true,
+ searchable: true,
+ subType: {
+ nested: {
+ path: 'nestedField',
+ },
+ },
+ type: 'string',
+ },
+ ];
+
describe('fields_for_wildcard_route response', () => {
before(() => esArchiver.load('index_patterns/basic_index'));
after(() => esArchiver.unload('index_patterns/basic_index'));
@@ -26,54 +75,7 @@ export default function ({ getService }) {
.get('/api/index_patterns/_fields_for_wildcard')
.query({ pattern: 'basic_index' })
.expect(200, {
- fields: [
- {
- type: 'boolean',
- esTypes: ['boolean'],
- searchable: true,
- aggregatable: true,
- name: 'bar',
- readFromDocValues: true,
- },
- {
- type: 'string',
- esTypes: ['text'],
- searchable: true,
- aggregatable: false,
- name: 'baz',
- readFromDocValues: false,
- },
- {
- type: 'string',
- esTypes: ['keyword'],
- searchable: true,
- aggregatable: true,
- name: 'baz.keyword',
- readFromDocValues: true,
- subType: { multi: { parent: 'baz' } },
- },
- {
- type: 'number',
- esTypes: ['long'],
- searchable: true,
- aggregatable: true,
- name: 'foo',
- readFromDocValues: true,
- },
- {
- aggregatable: true,
- esTypes: ['keyword'],
- name: 'nestedField.child',
- readFromDocValues: true,
- searchable: true,
- subType: {
- nested: {
- path: 'nestedField',
- },
- },
- type: 'string',
- },
- ],
+ fields: testFields,
})
.then(ensureFieldsAreSorted);
});
@@ -162,11 +164,19 @@ export default function ({ getService }) {
.then(ensureFieldsAreSorted);
});
- it('returns 404 when the pattern does not exist', async () => {
+ it('returns fields when one pattern exists and the other does not', async () => {
+ await supertest
+ .get('/api/index_patterns/_fields_for_wildcard')
+ .query({ pattern: 'bad_index,basic_index' })
+ .expect(200, {
+ fields: testFields,
+ });
+ });
+ it('returns 404 when no patterns exist', async () => {
await supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
- pattern: '[non-existing-pattern]its-invalid-*',
+ pattern: 'bad_index',
})
.expect(404);
});