Index patterns api - load field list on server (#82629)

The server side index patterns api can now load the field list. Previously it would error if a field list refresh was required.

The regular and rollup index pattern field list loading methods were merged. Rollup index patterns require additional functionality over regular index patterns when it comes to loading the field list, but this won't be necessary once rollups v2 is released.
This commit is contained in:
Matthew Kime 2020-11-09 17:11:21 -06:00 committed by GitHub
parent bf758312cd
commit 7a67cffe37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 484 additions and 394 deletions

View file

@ -9,5 +9,5 @@ Get field list by providing an index patttern (or spec)
<b>Signature:</b>
```typescript
getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions) => Promise<any>;
getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions | undefined) => Promise<any>;
```

View file

@ -9,5 +9,5 @@ Get field list by providing { pattern }
<b>Signature:</b>
```typescript
getFieldsForWildcard: (options?: GetFieldsOptions) => Promise<any>;
getFieldsForWildcard: (options: GetFieldsOptions) => Promise<any>;
```

View file

@ -26,8 +26,8 @@ export declare class IndexPatternsService
| [get](./kibana-plugin-plugins-data-public.indexpatternsservice.get.md) | | <code>(id: string) =&gt; Promise&lt;IndexPattern&gt;</code> | Get an index pattern by id. Cache optimized |
| [getCache](./kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md) | | <code>() =&gt; Promise&lt;SavedObject&lt;IndexPatternSavedObjectAttrs&gt;[] &#124; null &#124; undefined&gt;</code> | |
| [getDefault](./kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md) | | <code>() =&gt; Promise&lt;IndexPattern &#124; null&gt;</code> | Get default index pattern |
| [getFieldsForIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md) | | <code>(indexPattern: IndexPattern &#124; IndexPatternSpec, options?: GetFieldsOptions) =&gt; Promise&lt;any&gt;</code> | Get field list by providing an index patttern (or spec) |
| [getFieldsForWildcard](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md) | | <code>(options?: GetFieldsOptions) =&gt; Promise&lt;any&gt;</code> | Get field list by providing { pattern } |
| [getFieldsForIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md) | | <code>(indexPattern: IndexPattern &#124; IndexPatternSpec, options?: GetFieldsOptions &#124; undefined) =&gt; Promise&lt;any&gt;</code> | Get field list by providing an index patttern (or spec) |
| [getFieldsForWildcard](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md) | | <code>(options: GetFieldsOptions) =&gt; Promise&lt;any&gt;</code> | Get field list by providing { pattern } |
| [getIds](./kibana-plugin-plugins-data-public.indexpatternsservice.getids.md) | | <code>(refresh?: boolean) =&gt; Promise&lt;string[]&gt;</code> | Get list of index pattern ids |
| [getIdsWithTitle](./kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md) | | <code>(refresh?: boolean) =&gt; Promise&lt;Array&lt;{</code><br/><code> id: string;</code><br/><code> title: string;</code><br/><code> }&gt;&gt;</code> | Get list of index pattern ids with titles |
| [getTitles](./kibana-plugin-plugins-data-public.indexpatternsservice.gettitles.md) | | <code>(refresh?: boolean) =&gt; Promise&lt;string[]&gt;</code> | Get list of index pattern titles |

View file

@ -0,0 +1,28 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [getCapabilitiesForRollupIndices](./kibana-plugin-plugins-data-server.getcapabilitiesforrollupindices.md)
## getCapabilitiesForRollupIndices() function
<b>Signature:</b>
```typescript
export declare function getCapabilitiesForRollupIndices(indices: {
[key: string]: any;
}): {
[key: string]: any;
};
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| indices | <code>{</code><br/><code> [key: string]: any;</code><br/><code>}</code> | |
<b>Returns:</b>
`{
[key: string]: any;
}`

View file

@ -15,6 +15,8 @@ getFieldsForWildcard(options: {
fieldCapsOptions?: {
allow_no_indices: boolean;
};
type?: string;
rollupIndex?: string;
}): Promise<FieldDescriptor[]>;
```
@ -22,7 +24,7 @@ getFieldsForWildcard(options: {
| Parameter | Type | Description |
| --- | --- | --- |
| options | <code>{</code><br/><code> pattern: string &#124; string[];</code><br/><code> metaFields?: string[];</code><br/><code> fieldCapsOptions?: {</code><br/><code> allow_no_indices: boolean;</code><br/><code> };</code><br/><code> }</code> | |
| options | <code>{</code><br/><code> pattern: string &#124; string[];</code><br/><code> metaFields?: string[];</code><br/><code> fieldCapsOptions?: {</code><br/><code> allow_no_indices: boolean;</code><br/><code> };</code><br/><code> type?: string;</code><br/><code> rollupIndex?: string;</code><br/><code> }</code> | |
<b>Returns:</b>

View file

@ -8,7 +8,7 @@
```typescript
start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): {
indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract) => Promise<IndexPatternsCommonService>;
indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract, elasticsearchClient: ElasticsearchClient) => Promise<IndexPatternsCommonService>;
};
```
@ -22,6 +22,6 @@ start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps):
<b>Returns:</b>
`{
indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract) => Promise<IndexPatternsCommonService>;
indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract, elasticsearchClient: ElasticsearchClient) => Promise<IndexPatternsCommonService>;
}`

View file

@ -28,6 +28,7 @@
| Function | Description |
| --- | --- |
| [getCapabilitiesForRollupIndices(indices)](./kibana-plugin-plugins-data-server.getcapabilitiesforrollupindices.md) | |
| [getDefaultSearchParams(uiSettingsClient)](./kibana-plugin-plugins-data-server.getdefaultsearchparams.md) | |
| [getShardTimeout(config)](./kibana-plugin-plugins-data-server.getshardtimeout.md) | |
| [getTime(indexPattern, timeRange, options)](./kibana-plugin-plugins-data-server.gettime.md) | |
@ -77,6 +78,7 @@
| [esQuery](./kibana-plugin-plugins-data-server.esquery.md) | |
| [fieldFormats](./kibana-plugin-plugins-data-server.fieldformats.md) | |
| [indexPatterns](./kibana-plugin-plugins-data-server.indexpatterns.md) | |
| [mergeCapabilitiesWithFields](./kibana-plugin-plugins-data-server.mergecapabilitieswithfields.md) | |
| [search](./kibana-plugin-plugins-data-server.search.md) | |
| [UI\_SETTINGS](./kibana-plugin-plugins-data-server.ui_settings.md) | |

View file

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [mergeCapabilitiesWithFields](./kibana-plugin-plugins-data-server.mergecapabilitieswithfields.md)
## mergeCapabilitiesWithFields variable
<b>Signature:</b>
```typescript
mergeCapabilitiesWithFields: (rollupIndexCapabilities: {
[key: string]: any;
}, fieldsFromFieldCapsApi: {
[key: string]: any;
}, previousFields?: FieldDescriptor[]) => FieldDescriptor[]
```

View file

@ -12,7 +12,7 @@ start(core: CoreStart): {
fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
};
indexPatterns: {
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("src/core/server").SavedObjectsClient, "update" | "find" | "get" | "delete" | "errors" | "create" | "bulkCreate" | "checkConflicts" | "bulkGet" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo">) => Promise<import("../common").IndexPatternsService>;
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("src/core/server").SavedObjectsClient, "update" | "find" | "get" | "delete" | "errors" | "create" | "bulkCreate" | "checkConflicts" | "bulkGet" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo">, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise<import("../common").IndexPatternsService>;
};
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
};
@ -31,7 +31,7 @@ start(core: CoreStart): {
fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
};
indexPatterns: {
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("src/core/server").SavedObjectsClient, "update" | "find" | "get" | "delete" | "errors" | "create" | "bulkCreate" | "checkConflicts" | "bulkGet" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo">) => Promise<import("../common").IndexPatternsService>;
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("src/core/server").SavedObjectsClient, "update" | "find" | "get" | "delete" | "errors" | "create" | "bulkCreate" | "checkConflicts" | "bulkGet" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo">, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise<import("../common").IndexPatternsService>;
};
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
}`

View file

@ -98,11 +98,12 @@ export class IndexPatternsService {
* Refresh cache of index pattern ids and titles
*/
private async refreshSavedObjectsCache() {
this.savedObjectsCache = await this.savedObjectsClient.find<IndexPatternSavedObjectAttrs>({
const so = await this.savedObjectsClient.find<IndexPatternSavedObjectAttrs>({
type: 'index-pattern',
fields: ['title'],
perPage: 10000,
});
this.savedObjectsCache = so;
}
/**
@ -215,13 +216,13 @@ export class IndexPatternsService {
* Get field list by providing { pattern }
* @param options
*/
getFieldsForWildcard = async (options: GetFieldsOptions = {}) => {
getFieldsForWildcard = async (options: GetFieldsOptions) => {
const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS);
return this.apiClient.getFieldsForWildcard({
pattern: options.pattern,
metaFields,
type: options.type,
params: options.params || {},
rollupIndex: options.rollupIndex,
});
};
@ -231,13 +232,13 @@ export class IndexPatternsService {
*/
getFieldsForIndexPattern = async (
indexPattern: IndexPattern | IndexPatternSpec,
options: GetFieldsOptions = {}
options?: GetFieldsOptions
) =>
this.getFieldsForWildcard({
pattern: indexPattern.title as string,
...options,
type: indexPattern.type,
params: indexPattern.typeMeta && indexPattern.typeMeta.params,
rollupIndex: indexPattern?.typeMeta?.params?.rollup_index,
...options,
pattern: indexPattern.title as string,
});
/**
@ -374,10 +375,10 @@ export class IndexPatternsService {
try {
spec.fields = isFieldRefreshRequired
? await this.refreshFieldSpecMap(spec.fields || {}, id, spec.title as string, {
pattern: title,
pattern: title as string,
metaFields: await this.config.get(UI_SETTINGS.META_FIELDS),
type,
params: typeMeta && typeMeta.params,
rollupIndex: typeMeta?.params?.rollupIndex,
})
: spec.fields;
} catch (err) {

View file

@ -86,15 +86,22 @@ export interface SavedObjectsClientCommon {
}
export interface GetFieldsOptions {
pattern?: string;
pattern: string;
type?: string;
params?: any;
lookBack?: boolean;
metaFields?: string[];
rollupIndex?: string;
}
export interface GetFieldsOptionsTimePattern {
pattern: string;
metaFields: string[];
lookBack: number;
interval: string;
}
export interface IIndexPatternsApiClient {
getFieldsForTimePattern: (options: GetFieldsOptions) => Promise<any>;
getFieldsForTimePattern: (options: GetFieldsOptionsTimePattern) => Promise<any>;
getFieldsForWildcard: (options: GetFieldsOptions) => Promise<any>;
}

View file

@ -32,7 +32,12 @@ describe('IndexPatternsApiClient', () => {
test('uses the right URI to fetch fields for time patterns', async function () {
const expectedPath = '/api/index_patterns/_fields_for_time_pattern';
await indexPatternsApiClient.getFieldsForTimePattern();
await indexPatternsApiClient.getFieldsForTimePattern({
pattern: 'blah',
metaFields: [],
lookBack: 5,
interval: '',
});
expect(fetchSpy).toHaveBeenCalledWith(expectedPath, expect.any(Object));
});
@ -40,15 +45,7 @@ describe('IndexPatternsApiClient', () => {
test('uses the right URI to fetch fields for wildcard', async function () {
const expectedPath = '/api/index_patterns/_fields_for_wildcard';
await indexPatternsApiClient.getFieldsForWildcard();
expect(fetchSpy).toHaveBeenCalledWith(expectedPath, expect.any(Object));
});
test('uses the right URI to fetch fields for wildcard given a type', async function () {
const expectedPath = '/api/index_patterns/rollup/_fields_for_wildcard';
await indexPatternsApiClient.getFieldsForWildcard({ type: 'rollup' });
await indexPatternsApiClient.getFieldsForWildcard({ pattern: 'blah' });
expect(fetchSpy).toHaveBeenCalledWith(expectedPath, expect.any(Object));
});

View file

@ -19,7 +19,11 @@
import { HttpSetup } from 'src/core/public';
import { IndexPatternMissingIndices } from '../../../common/index_patterns/lib';
import { GetFieldsOptions, IIndexPatternsApiClient } from '../../../common/index_patterns/types';
import {
GetFieldsOptions,
IIndexPatternsApiClient,
GetFieldsOptionsTimePattern,
} from '../../../common/index_patterns/types';
const API_BASE_URL: string = `/api/index_patterns/`;
@ -48,7 +52,7 @@ export class IndexPatternsApiClient implements IIndexPatternsApiClient {
return API_BASE_URL + path.filter(Boolean).map(encodeURIComponent).join('/');
}
getFieldsForTimePattern(options: GetFieldsOptions = {}) {
getFieldsForTimePattern(options: GetFieldsOptionsTimePattern) {
const { pattern, lookBack, metaFields } = options;
const url = this._getUrl(['_fields_for_time_pattern']);
@ -60,27 +64,12 @@ export class IndexPatternsApiClient implements IIndexPatternsApiClient {
}).then((resp: any) => resp.fields);
}
getFieldsForWildcard(options: GetFieldsOptions = {}) {
const { pattern, metaFields, type, params } = options;
let url;
let query;
if (type) {
url = this._getUrl([type, '_fields_for_wildcard']);
query = {
pattern,
meta_fields: metaFields,
params: JSON.stringify(params),
};
} else {
url = this._getUrl(['_fields_for_wildcard']);
query = {
pattern,
meta_fields: metaFields,
};
}
return this._request(url, query).then((resp: any) => resp.fields);
getFieldsForWildcard({ pattern, metaFields, type, rollupIndex }: GetFieldsOptions) {
return this._request(this._getUrl(['_fields_for_wildcard']), {
pattern,
meta_fields: metaFields,
type,
rollup_index: rollupIndex,
}).then((resp: any) => resp.fields);
}
}

View file

@ -1367,9 +1367,9 @@ export class IndexPatternsService {
// (undocumented)
getCache: () => Promise<SavedObject<IndexPatternSavedObjectAttrs>[] | null | undefined>;
getDefault: () => Promise<IndexPattern | null>;
getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions) => Promise<any>;
getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions | undefined) => Promise<any>;
// Warning: (ae-forgotten-export) The symbol "GetFieldsOptions" needs to be exported by the entry point index.d.ts
getFieldsForWildcard: (options?: GetFieldsOptions) => Promise<any>;
getFieldsForWildcard: (options: GetFieldsOptions) => Promise<any>;
getIds: (refresh?: boolean) => Promise<string[]>;
getIdsWithTitle: (refresh?: boolean) => Promise<Array<{
id: string;

View file

@ -134,6 +134,8 @@ export {
FieldDescriptor as IndexPatternFieldDescriptor,
shouldReadFieldFromDocValues, // used only in logstash_fields fixture
FieldDescriptor,
mergeCapabilitiesWithFields,
getCapabilitiesForRollupIndices,
} from './index_patterns';
export {

View file

@ -18,4 +18,8 @@
*/
export * from './index_patterns_fetcher';
export { shouldReadFieldFromDocValues } from './lib';
export {
shouldReadFieldFromDocValues,
mergeCapabilitiesWithFields,
getCapabilitiesForRollupIndices,
} from './lib';

View file

@ -18,8 +18,15 @@
*/
import { ElasticsearchClient } from 'kibana/server';
import { keyBy } from 'lodash';
import { getFieldCapabilities, resolveTimePattern, createNoMatchingIndicesError } from './lib';
import {
getFieldCapabilities,
resolveTimePattern,
createNoMatchingIndicesError,
getCapabilitiesForRollupIndices,
mergeCapabilitiesWithFields,
} from './lib';
export interface FieldDescriptor {
aggregatable: boolean;
@ -58,11 +65,44 @@ export class IndexPatternsFetcher {
pattern: string | string[];
metaFields?: string[];
fieldCapsOptions?: { allow_no_indices: boolean };
type?: string;
rollupIndex?: string;
}): Promise<FieldDescriptor[]> {
const { pattern, metaFields, fieldCapsOptions } = options;
return await getFieldCapabilities(this.elasticsearchClient, pattern, metaFields, {
allow_no_indices: fieldCapsOptions ? fieldCapsOptions.allow_no_indices : this.allowNoIndices,
});
const { pattern, metaFields, fieldCapsOptions, type, rollupIndex } = options;
const fieldCapsResponse = await getFieldCapabilities(
this.elasticsearchClient,
pattern,
metaFields,
{
allow_no_indices: fieldCapsOptions
? fieldCapsOptions.allow_no_indices
: this.allowNoIndices,
}
);
if (type === 'rollup' && rollupIndex) {
const rollupFields: FieldDescriptor[] = [];
const rollupIndexCapabilities = getCapabilitiesForRollupIndices(
(
await this.elasticsearchClient.rollup.getRollupIndexCaps({
index: rollupIndex,
})
).body
)[rollupIndex].aggs;
const fieldCapsResponseObj = keyBy(fieldCapsResponse, 'name');
// Keep meta fields
metaFields!.forEach(
(field: string) =>
fieldCapsResponseObj[field] && rollupFields.push(fieldCapsResponseObj[field])
);
return mergeCapabilitiesWithFields(
rollupIndexCapabilities,
fieldCapsResponseObj,
rollupFields
);
}
return fieldCapsResponse;
}
/**

View file

@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { jobs } from './jobs';

View file

@ -1,7 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export const jobs = [

View file

@ -1,8 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import { areJobsCompatible, mergeJobConfigurations } from '../jobs_compatibility';
import { jobs } from './fixtures';

View file

@ -20,3 +20,5 @@
export { getFieldCapabilities, shouldReadFieldFromDocValues } from './field_capabilities';
export { resolveTimePattern } from './resolve_time_pattern';
export { createNoMatchingIndicesError } from './errors';
export * from './merge_capabilities_with_fields';
export * from './map_capabilities';

View file

@ -1,7 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { isEqual } from 'lodash';

View file

@ -0,0 +1,37 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { mergeJobConfigurations } from './jobs_compatibility';
export function getCapabilitiesForRollupIndices(indices: { [key: string]: any }) {
const indexNames = Object.keys(indices);
const capabilities = {} as { [key: string]: any };
indexNames.forEach((index) => {
try {
capabilities[index] = mergeJobConfigurations(indices[index].rollup_jobs);
} catch (e) {
capabilities[index] = {
error: e.message,
};
}
});
return capabilities;
}

View file

@ -1,20 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// Merge rollup capabilities information with field information
export interface Field {
name?: string;
[key: string]: any;
}
import { FieldDescriptor } from '../index_patterns_fetcher';
export const mergeCapabilitiesWithFields = (
rollupIndexCapabilities: { [key: string]: any },
fieldsFromFieldCapsApi: { [key: string]: any },
previousFields: Field[] = []
previousFields: FieldDescriptor[] = []
) => {
const rollupFields = [...previousFields];
const rollupFieldNames: string[] = [];

View file

@ -17,5 +17,11 @@
* under the License.
*/
export * from './utils';
export { IndexPatternsFetcher, FieldDescriptor, shouldReadFieldFromDocValues } from './fetcher';
export {
IndexPatternsFetcher,
FieldDescriptor,
shouldReadFieldFromDocValues,
mergeCapabilitiesWithFields,
getCapabilitiesForRollupIndices,
} from './fetcher';
export { IndexPatternsService, IndexPatternsServiceStart } from './index_patterns_service';

View file

@ -17,13 +17,30 @@
* under the License.
*/
import { GetFieldsOptions, IIndexPatternsApiClient } from '../../common/index_patterns/types';
import { ElasticsearchClient } from 'kibana/server';
import {
GetFieldsOptions,
IIndexPatternsApiClient,
GetFieldsOptionsTimePattern,
} from '../../common/index_patterns/types';
import { IndexPatternsFetcher } from './fetcher';
export class IndexPatternsApiServer implements IIndexPatternsApiClient {
async getFieldsForTimePattern(options: GetFieldsOptions = {}) {
throw new Error('IndexPatternsApiServer - getFieldsForTimePattern not defined');
esClient: ElasticsearchClient;
constructor(elasticsearchClient: ElasticsearchClient) {
this.esClient = elasticsearchClient;
}
async getFieldsForWildcard(options: GetFieldsOptions = {}) {
throw new Error('IndexPatternsApiServer - getFieldsForWildcard not defined');
async getFieldsForWildcard({ pattern, metaFields, type, rollupIndex }: GetFieldsOptions) {
const indexPatterns = new IndexPatternsFetcher(this.esClient);
return await indexPatterns.getFieldsForWildcard({
pattern,
metaFields,
type,
rollupIndex,
});
}
async getFieldsForTimePattern(options: GetFieldsOptionsTimePattern) {
const indexPatterns = new IndexPatternsFetcher(this.esClient);
return await indexPatterns.getFieldsForTimePattern(options);
}
}

View file

@ -17,7 +17,14 @@
* under the License.
*/
import { CoreSetup, CoreStart, Plugin, Logger, SavedObjectsClientContract } from 'kibana/server';
import {
CoreSetup,
CoreStart,
Plugin,
Logger,
SavedObjectsClientContract,
ElasticsearchClient,
} from 'kibana/server';
import { registerRoutes } from './routes';
import { indexPatternSavedObjectType } from '../saved_objects';
import { capabilitiesProvider } from './capabilities_provider';
@ -29,7 +36,8 @@ import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper
export interface IndexPatternsServiceStart {
indexPatternsServiceFactory: (
savedObjectsClient: SavedObjectsClientContract
savedObjectsClient: SavedObjectsClientContract,
elasticsearchClient: ElasticsearchClient
) => Promise<IndexPatternsCommonService>;
}
@ -50,14 +58,17 @@ export class IndexPatternsService implements Plugin<void, IndexPatternsServiceSt
const { uiSettings } = core;
return {
indexPatternsServiceFactory: async (savedObjectsClient: SavedObjectsClientContract) => {
indexPatternsServiceFactory: async (
savedObjectsClient: SavedObjectsClientContract,
elasticsearchClient: ElasticsearchClient
) => {
const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient);
return new IndexPatternsCommonService({
uiSettings: new UiSettingsServerToCommon(uiSettingsClient),
savedObjectsClient: new SavedObjectsClientServerToCommon(savedObjectsClient),
apiClient: new IndexPatternsApiServer(),
apiClient: new IndexPatternsApiServer(elasticsearchClient),
fieldFormats: formats,
onError: (error) => {
logger.error(error);

View file

@ -42,13 +42,15 @@ export function registerRoutes(http: HttpServiceSetup) {
meta_fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
defaultValue: [],
}),
type: schema.maybe(schema.string()),
rollup_index: schema.maybe(schema.string()),
}),
},
},
async (context, request, response) => {
const { asCurrentUser } = context.core.elasticsearch.client;
const indexPatterns = new IndexPatternsFetcher(asCurrentUser);
const { pattern, meta_fields: metaFields } = request.query;
const { pattern, meta_fields: metaFields, type, rollup_index: rollupIndex } = request.query;
let parsedFields: string[] = [];
try {
@ -61,6 +63,8 @@ export function registerRoutes(http: HttpServiceSetup) {
const fields = await indexPatterns.getFieldsForWildcard({
pattern,
metaFields: parsedFields,
type,
rollupIndex,
});
return response.ok({

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { KibanaRequest } from 'src/core/server';
import { KibanaRequest, ElasticsearchClient } from 'src/core/server';
import { coreMock } from '../../../../../core/server/mocks';
import { expressionsPluginMock } from '../../../../../plugins/expressions/server/mocks';
@ -63,7 +63,8 @@ describe('AggsService - server', () => {
expect(start).toHaveProperty('asScopedToClient');
const contract = await start.asScopedToClient(
savedObjects.getScopedClient({} as KibanaRequest)
savedObjects.getScopedClient({} as KibanaRequest),
{} as ElasticsearchClient
);
expect(contract).toHaveProperty('calculateAutoTimeExpression');
expect(contract).toHaveProperty('createAggConfigs');
@ -74,7 +75,10 @@ describe('AggsService - server', () => {
service.setup(setupDeps);
const start = await service
.start(startDeps)
.asScopedToClient(savedObjects.getScopedClient({} as KibanaRequest));
.asScopedToClient(
savedObjects.getScopedClient({} as KibanaRequest),
{} as ElasticsearchClient
);
expect(start.types.get('terms').name).toBe('terms');
});
@ -83,7 +87,10 @@ describe('AggsService - server', () => {
service.setup(setupDeps);
const start = await service
.start(startDeps)
.asScopedToClient(savedObjects.getScopedClient({} as KibanaRequest));
.asScopedToClient(
savedObjects.getScopedClient({} as KibanaRequest),
{} as ElasticsearchClient
);
const aggTypes = getAggTypes();
expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length);
@ -103,7 +110,10 @@ describe('AggsService - server', () => {
const start = await service
.start(startDeps)
.asScopedToClient(savedObjects.getScopedClient({} as KibanaRequest));
.asScopedToClient(
savedObjects.getScopedClient({} as KibanaRequest),
{} as ElasticsearchClient
);
const aggTypes = getAggTypes();
expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length + 1);

View file

@ -19,7 +19,11 @@
import { pick } from 'lodash';
import { UiSettingsServiceStart, SavedObjectsClientContract } from 'src/core/server';
import {
UiSettingsServiceStart,
SavedObjectsClientContract,
ElasticsearchClient,
} from 'src/core/server';
import { ExpressionsServiceSetup } from 'src/plugins/expressions/common';
import {
AggsCommonService,
@ -65,7 +69,10 @@ export class AggsService {
public start({ fieldFormats, uiSettings, indexPatterns }: AggsStartDependencies): AggsStart {
return {
asScopedToClient: async (savedObjectsClient: SavedObjectsClientContract) => {
asScopedToClient: async (
savedObjectsClient: SavedObjectsClientContract,
elasticsearchClient: ElasticsearchClient
) => {
const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient);
@ -82,8 +89,9 @@ export class AggsService {
types,
} = this.aggsCommonService.start({
getConfig,
getIndexPattern: (await indexPatterns.indexPatternsServiceFactory(savedObjectsClient))
.get,
getIndexPattern: (
await indexPatterns.indexPatternsServiceFactory(savedObjectsClient, elasticsearchClient)
).get,
isDefaultTimezone,
});

View file

@ -17,11 +17,14 @@
* under the License.
*/
import { SavedObjectsClientContract } from 'src/core/server';
import { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/server';
import { AggsCommonSetup, AggsStart as Start } from '../../../common';
export type AggsSetup = AggsCommonSetup;
export interface AggsStart {
asScopedToClient: (savedObjectsClient: SavedObjectsClientContract) => Promise<Start>;
asScopedToClient: (
savedObjectsClient: SavedObjectsClientContract,
elasticsearchClient: ElasticsearchClient
) => Promise<Start>;
}

View file

@ -177,7 +177,11 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
const { elasticsearch, savedObjects, uiSettings } = core;
const asScoped = this.asScopedProvider(core);
return {
aggs: this.aggsService.start({ fieldFormats, uiSettings, indexPatterns }),
aggs: this.aggsService.start({
fieldFormats,
uiSettings,
indexPatterns,
}),
getSearchStrategy: this.getSearchStrategy,
asScoped,
searchSource: {
@ -185,7 +189,8 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
const esClient = elasticsearch.client.asScoped(request);
const savedObjectsClient = savedObjects.getScopedClient(request);
const scopedIndexPatterns = await indexPatterns.indexPatternsServiceFactory(
savedObjectsClient
savedObjectsClient,
esClient.asCurrentUser
);
const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);

View file

@ -17,7 +17,8 @@ import { CoreStart as CoreStart_2 } from 'kibana/server';
import { Datatable } from 'src/plugins/expressions/common';
import { DatatableColumn } from 'src/plugins/expressions';
import { Duration } from 'moment';
import { ElasticsearchClient } from 'kibana/server';
import { ElasticsearchClient } from 'src/core/server';
import { ElasticsearchClient as ElasticsearchClient_2 } from 'kibana/server';
import { Ensure } from '@kbn/utility-types';
import { EnvironmentMode } from '@kbn/config';
import { ErrorToastOptions } from 'src/core/public/notifications';
@ -389,6 +390,15 @@ export type Filter = {
query?: any;
};
// Warning: (ae-missing-release-tag) "getCapabilitiesForRollupIndices" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function getCapabilitiesForRollupIndices(indices: {
[key: string]: any;
}): {
[key: string]: any;
};
// Warning: (ae-forgotten-export) The symbol "IUiSettingsClient" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "getDefaultSearchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -688,7 +698,7 @@ export const indexPatterns: {
//
// @public (undocumented)
export class IndexPatternsFetcher {
constructor(elasticsearchClient: ElasticsearchClient, allowNoIndices?: boolean);
constructor(elasticsearchClient: ElasticsearchClient_2, allowNoIndices?: boolean);
getFieldsForTimePattern(options: {
pattern: string;
metaFields: string[];
@ -701,6 +711,8 @@ export class IndexPatternsFetcher {
fieldCapsOptions?: {
allow_no_indices: boolean;
};
type?: string;
rollupIndex?: string;
}): Promise<FieldDescriptor[]>;
}
@ -715,7 +727,7 @@ export class IndexPatternsService implements Plugin_3<void, IndexPatternsService
//
// (undocumented)
start(core: CoreStart_2, { fieldFormats, logger }: IndexPatternsServiceStartDeps): {
indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract_2) => Promise<IndexPatternsService_2>;
indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract_2, elasticsearchClient: ElasticsearchClient_2) => Promise<IndexPatternsService_2>;
};
}
@ -824,6 +836,15 @@ export interface KueryNode {
type: keyof NodeTypes;
}
// Warning: (ae-missing-release-tag) "mergeCapabilitiesWithFields" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const mergeCapabilitiesWithFields: (rollupIndexCapabilities: {
[key: string]: any;
}, fieldsFromFieldCapsApi: {
[key: string]: any;
}, previousFields?: FieldDescriptor[]) => FieldDescriptor[];
// Warning: (ae-missing-release-tag) "METRIC_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@ -927,7 +948,7 @@ export class Plugin implements Plugin_2<PluginSetup, PluginStart, DataPluginSetu
fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
};
indexPatterns: {
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("src/core/server").SavedObjectsClient, "update" | "find" | "get" | "delete" | "errors" | "create" | "bulkCreate" | "checkConflicts" | "bulkGet" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo">) => Promise<import("../common").IndexPatternsService>;
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("src/core/server").SavedObjectsClient, "update" | "find" | "get" | "delete" | "errors" | "create" | "bulkCreate" | "checkConflicts" | "bulkGet" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo">, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise<import("../common").IndexPatternsService>;
};
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
};
@ -1195,22 +1216,22 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:256:5 - (ae-forgotten-export) The symbol "getTotalLoaded" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:257:5 - (ae-forgotten-export) The symbol "toSnakeCase" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:261:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:272:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:273:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:277:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:278:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:282:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:285:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index_patterns/index_patterns_service.ts:50:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:258:5 - (ae-forgotten-export) The symbol "getTotalLoaded" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:259:5 - (ae-forgotten-export) The symbol "toSnakeCase" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:264:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:273:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:274:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:275:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:279:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:280:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:284:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:287:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index_patterns/index_patterns_service.ts:58:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/plugin.ts:88:66 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/search/types.ts:104:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts

View file

@ -62,10 +62,12 @@ export async function getFields(
let indexPatternString = indexPattern;
if (!indexPatternString) {
const [{ savedObjects }, { data }] = await framework.core.getStartServices();
const [{ savedObjects, elasticsearch }, { data }] = await framework.core.getStartServices();
const savedObjectsClient = savedObjects.getScopedClient(request);
const clusterClient = elasticsearch.client.asScoped(request).asCurrentUser;
const indexPatternsService = await data.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient
savedObjectsClient,
clusterClient
);
const defaultIndexPattern = await indexPatternsService.getDefault();
indexPatternString = get(defaultIndexPattern, 'title', '');

View file

@ -58,14 +58,16 @@ export class DataSearchTestPlugin
},
},
async (context, req, res) => {
const [{ savedObjects }, { data }] = await core.getStartServices();
const [{ savedObjects, elasticsearch }, { data }] = await core.getStartServices();
const service = await data.search.searchSource.asScoped(req);
const clusterClient = elasticsearch.client.asScoped(req).asCurrentUser;
const savedObjectsClient = savedObjects.getScopedClient(req);
// Since the index pattern ID can change on each test run, we need
// to look it up on the fly and insert it into the request.
const indexPatterns = await data.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient
savedObjectsClient,
clusterClient
);
const ids = await indexPatterns.getIds();
// @ts-expect-error Force overwriting the request

View file

@ -36,13 +36,35 @@ export class IndexPatternsTestPlugin
public setup(core: CoreSetup<IndexPatternsTestStartDeps>) {
const router = core.http.createRouter();
router.post(
{
path: '/api/index-patterns-plugin/create',
validate: {
body: schema.object({}, { unknowns: 'allow' }),
},
},
async (context, req, res) => {
const [{ savedObjects, elasticsearch }, { data }] = await core.getStartServices();
const savedObjectsClient = savedObjects.getScopedClient(req);
const service = await data.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearch.client.asScoped(req).asCurrentUser
);
const ids = await service.createAndSave(req.body);
return res.ok({ body: ids });
}
);
router.get(
{ path: '/api/index-patterns-plugin/get-all', validate: false },
async (context, req, res) => {
const [{ savedObjects }, { data }] = await core.getStartServices();
const [{ savedObjects, elasticsearch }, { data }] = await core.getStartServices();
const savedObjectsClient = savedObjects.getScopedClient(req);
const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient);
const ids = await service.getIds();
const service = await data.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearch.client.asScoped(req).asCurrentUser
);
const ids = await service.getIds(true);
return res.ok({ body: ids });
}
);
@ -58,9 +80,12 @@ export class IndexPatternsTestPlugin
},
async (context, req, res) => {
const id = (req.params as Record<string, string>).id;
const [{ savedObjects }, { data }] = await core.getStartServices();
const [{ savedObjects, elasticsearch }, { data }] = await core.getStartServices();
const savedObjectsClient = savedObjects.getScopedClient(req);
const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient);
const service = await data.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearch.client.asScoped(req).asCurrentUser
);
const ip = await service.get(id);
return res.ok({ body: ip.toSpec() });
}
@ -76,10 +101,13 @@ export class IndexPatternsTestPlugin
},
},
async (context, req, res) => {
const [{ savedObjects }, { data }] = await core.getStartServices();
const [{ savedObjects, elasticsearch }, { data }] = await core.getStartServices();
const id = (req.params as Record<string, string>).id;
const savedObjectsClient = savedObjects.getScopedClient(req);
const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient);
const service = await data.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearch.client.asScoped(req).asCurrentUser
);
const ip = await service.get(id);
await service.updateSavedObject(ip);
return res.ok();
@ -96,10 +124,13 @@ export class IndexPatternsTestPlugin
},
},
async (context, req, res) => {
const [{ savedObjects }, { data }] = await core.getStartServices();
const [{ savedObjects, elasticsearch }, { data }] = await core.getStartServices();
const id = (req.params as Record<string, string>).id;
const savedObjectsClient = savedObjects.getScopedClient(req);
const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient);
const service = await data.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearch.client.asScoped(req).asCurrentUser
);
await service.delete(id);
return res.ok();
}

View file

@ -23,16 +23,25 @@ import '../../plugins/core_provider_plugin/types';
export default function ({ getService }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');
// skipping the tests as it deletes index patterns created by other test causing unexpected failures
// https://github.com/elastic/kibana/issues/79886
describe.skip('index patterns', function () {
describe('index patterns', function () {
let indexPatternId = '';
it('can get all ids', async () => {
const body = await (await supertest.get('/api/index-patterns-plugin/get-all').expect(200))
.body;
indexPatternId = body[0];
expect(body.length > 0).to.equal(true);
it('can create an index pattern', async () => {
const title = 'shakes*';
const fieldFormats = { bytes: { id: 'bytes' } };
const body = await (
await supertest
.post('/api/index-patterns-plugin/create')
.set('kbn-xsrf', 'anything')
.send({ title, fieldFormats })
.expect(200)
).body;
indexPatternId = body.id;
expect(body.id).not.empty();
expect(body.title).to.equal(title);
expect(body.fields.length).to.equal(15);
expect(body.fieldFormatMap).to.eql(fieldFormats);
});
it('can get index pattern by id', async () => {

View file

@ -161,9 +161,7 @@ export class RollupIndexPatternCreationConfig extends IndexPatternCreationConfig
getFetchForWildcardOptions = () => {
return {
type: this.type,
params: {
rollup_index: this.rollupIndex,
},
rollupIndex: this.rollupIndex,
};
};
}

View file

@ -1,7 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { jobs } from './jobs';

View file

@ -1,24 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { mergeJobConfigurations } from './jobs_compatibility';
export function getCapabilitiesForRollupIndices(indices: { [key: string]: any }) {
const indexNames = Object.keys(indices);
const capabilities = {} as { [key: string]: any };
indexNames.forEach((index) => {
try {
capabilities[index] = mergeJobConfigurations(indices[index].rollup_jobs);
} catch (e) {
capabilities[index] = {
error: e.message,
};
}
});
return capabilities;
}

View file

@ -6,8 +6,11 @@
import { keyBy, isString } from 'lodash';
import { ILegacyScopedClusterClient } from 'src/core/server';
import { ReqFacade } from '../../../../../../src/plugins/vis_type_timeseries/server';
import { mergeCapabilitiesWithFields } from '../merge_capabilities_with_fields';
import { getCapabilitiesForRollupIndices } from '../map_capabilities';
import {
mergeCapabilitiesWithFields,
getCapabilitiesForRollupIndices,
} from '../../../../../../src/plugins/data/server';
const getRollupIndices = (rollupData: { [key: string]: any }) => Object.keys(rollupData);

View file

@ -36,8 +36,7 @@ import { registerRollupSearchStrategy } from './lib/search_strategies';
import { elasticsearchJsPlugin } from './client/elasticsearch_rollup';
import { isEsError } from './shared_imports';
import { formatEsError } from './lib/format_es_error';
import { getCapabilitiesForRollupIndices } from './lib/map_capabilities';
import { mergeCapabilitiesWithFields } from './lib/merge_capabilities_with_fields';
import { getCapabilitiesForRollupIndices } from '../../../../src/plugins/data/server';
interface RollupContext {
client: ILegacyScopedClusterClient;
@ -107,7 +106,6 @@ export class RollupPlugin implements Plugin<void, void, any, any> {
isEsError,
formatEsError,
getCapabilitiesForRollupIndices,
mergeCapabilitiesWithFields,
},
sharedImports: {
IndexPatternsFetcher,

View file

@ -1,12 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { RouteDependencies } from '../../../types';
import { registerFieldsForWildcardRoute } from './register_fields_for_wildcard_route';
export function registerIndexPatternsRoutes(dependencies: RouteDependencies) {
registerFieldsForWildcardRoute(dependencies);
}

View file

@ -1,142 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { keyBy } from 'lodash';
import { schema } from '@kbn/config-schema';
import { Field } from '../../../lib/merge_capabilities_with_fields';
import { RouteDependencies } from '../../../types';
import type { IndexPatternsFetcher as IndexPatternsFetcherType } from '../../../../../../../src/plugins/data/server';
const parseMetaFields = (metaFields: string | string[]) => {
let parsedFields: string[] = [];
if (typeof metaFields === 'string') {
parsedFields = JSON.parse(metaFields);
} else {
parsedFields = metaFields;
}
return parsedFields;
};
const getFieldsForWildcardRequest = async (
context: any,
request: any,
response: any,
IndexPatternsFetcher: typeof IndexPatternsFetcherType
) => {
const { asCurrentUser } = context.core.elasticsearch.client;
const indexPatterns = new IndexPatternsFetcher(asCurrentUser);
const { pattern, meta_fields: metaFields } = request.query;
let parsedFields: string[] = [];
try {
parsedFields = parseMetaFields(metaFields);
} catch (error) {
return response.badRequest({
body: error,
});
}
try {
const fields = await indexPatterns.getFieldsForWildcard({
pattern,
metaFields: parsedFields,
});
return response.ok({
body: { fields },
headers: {
'content-type': 'application/json',
},
});
} catch (error) {
return response.notFound();
}
};
/**
* Get list of fields for rollup index pattern, in the format of regular index pattern fields
*/
export const registerFieldsForWildcardRoute = ({
router,
license,
lib: { isEsError, formatEsError, getCapabilitiesForRollupIndices, mergeCapabilitiesWithFields },
sharedImports: { IndexPatternsFetcher },
}: RouteDependencies) => {
const querySchema = schema.object({
pattern: schema.string(),
meta_fields: schema.arrayOf(schema.string(), {
defaultValue: [],
}),
params: schema.string({
validate(value) {
try {
const params = JSON.parse(value);
const keys = Object.keys(params);
const { rollup_index: rollupIndex } = params;
if (!rollupIndex) {
return '[request query.params]: "rollup_index" is required';
} else if (keys.length > 1) {
const invalidParams = keys.filter((key) => key !== 'rollup_index');
return `[request query.params]: ${invalidParams.join(', ')} is not allowed`;
}
} catch (err) {
return '[request query.params]: expected JSON string';
}
},
}),
});
router.get(
{
path: '/api/index_patterns/rollup/_fields_for_wildcard',
validate: {
query: querySchema,
},
},
license.guardApiRoute(async (context, request, response) => {
const { params, meta_fields: metaFields } = request.query;
try {
// Make call and use field information from response
const { payload } = await getFieldsForWildcardRequest(
context,
request,
response,
IndexPatternsFetcher
);
const fields = payload.fields;
const parsedParams = JSON.parse(params);
const rollupIndex = parsedParams.rollup_index;
const rollupFields: Field[] = [];
const fieldsFromFieldCapsApi: { [key: string]: any } = keyBy(fields, 'name');
const rollupIndexCapabilities = getCapabilitiesForRollupIndices(
await context.rollup!.client.callAsCurrentUser('rollup.rollupIndexCapabilities', {
indexPattern: rollupIndex,
})
)[rollupIndex].aggs;
// Keep meta fields
metaFields.forEach(
(field: string) =>
fieldsFromFieldCapsApi[field] && rollupFields.push(fieldsFromFieldCapsApi[field])
);
const mergedRollupFields = mergeCapabilitiesWithFields(
rollupIndexCapabilities,
fieldsFromFieldCapsApi,
rollupFields
);
return response.ok({ body: { fields: mergedRollupFields } });
} catch (err) {
if (isEsError(err)) {
return response.customError({ statusCode: err.statusCode, body: err });
}
return response.internalError({ body: err });
}
})
);
};

View file

@ -6,13 +6,11 @@
import { RouteDependencies } from '../types';
import { registerIndexPatternsRoutes } from './api/index_patterns';
import { registerIndicesRoutes } from './api/indices';
import { registerJobsRoutes } from './api/jobs';
import { registerSearchRoutes } from './api/search';
export function registerApiRoutes(dependencies: RouteDependencies) {
registerIndexPatternsRoutes(dependencies);
registerIndicesRoutes(dependencies);
registerJobsRoutes(dependencies);
registerSearchRoutes(dependencies);

View file

@ -8,6 +8,7 @@ import { IRouter } from 'src/core/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server';
import { getCapabilitiesForRollupIndices } from 'src/plugins/data/server';
import { IndexManagementPluginSetup } from '../../index_management/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
import { LicensingPluginSetup } from '../../licensing/server';
@ -15,8 +16,6 @@ import { License } from './services';
import { IndexPatternsFetcher } from './shared_imports';
import { isEsError } from './shared_imports';
import { formatEsError } from './lib/format_es_error';
import { getCapabilitiesForRollupIndices } from './lib/map_capabilities';
import { mergeCapabilitiesWithFields } from './lib/merge_capabilities_with_fields';
export interface Dependencies {
indexManagement?: IndexManagementPluginSetup;
@ -33,7 +32,6 @@ export interface RouteDependencies {
isEsError: typeof isEsError;
formatEsError: typeof formatEsError;
getCapabilitiesForRollupIndices: typeof getCapabilitiesForRollupIndices;
mergeCapabilitiesWithFields: typeof mergeCapabilitiesWithFields;
};
sharedImports: {
IndexPatternsFetcher: typeof IndexPatternsFetcher;

View file

@ -5,7 +5,7 @@
*/
export const API_BASE_PATH = '/api/rollup';
export const INDEX_PATTERNS_EXTENSION_BASE_PATH = '/api/index_patterns/rollup';
export const INDEX_PATTERNS_EXTENSION_BASE_PATH = '/api/index_patterns';
export const ROLLUP_INDEX_NAME = 'rollup_index';
export const INDEX_TO_ROLLUP_MAPPINGS = {
properties: {

View file

@ -26,7 +26,6 @@ export default function ({ getService }) {
describe('query params validation', () => {
let uri;
let body;
let params;
it('"pattern" is required', async () => {
uri = `${BASE_URI}`;
@ -36,62 +35,17 @@ export default function ({ getService }) {
);
});
it('"params" is required', async () => {
params = { pattern: 'foo' };
uri = `${BASE_URI}?${stringify(params, { sort: false })}`;
({ body } = await supertest.get(uri).expect(400));
expect(body.message).to.contain(
'[request query.params]: expected value of type [string]'
);
});
it('"params" must be a valid JSON string', async () => {
params = { pattern: 'foo', params: 'foobarbaz' };
uri = `${BASE_URI}?${stringify(params, { sort: false })}`;
({ body } = await supertest.get(uri).expect(400));
expect(body.message).to.contain('[request query.params]: expected JSON string');
});
it('"params" requires a "rollup_index" property', async () => {
params = { pattern: 'foo', params: JSON.stringify({}) };
uri = `${BASE_URI}?${stringify(params, { sort: false })}`;
({ body } = await supertest.get(uri).expect(400));
expect(body.message).to.contain('[request query.params]: "rollup_index" is required');
});
it('"params" only accepts a "rollup_index" property', async () => {
params = {
pattern: 'foo',
params: JSON.stringify({ rollup_index: 'my_index', someProp: 'bar' }),
};
uri = `${BASE_URI}?${stringify(params, { sort: false })}`;
({ body } = await supertest.get(uri).expect(400));
expect(body.message).to.contain('[request query.params]: someProp is not allowed');
});
it('"meta_fields" must be an Array', async () => {
params = {
pattern: 'foo',
params: JSON.stringify({ rollup_index: 'bar' }),
meta_fields: 'stringValue',
};
uri = `${BASE_URI}?${stringify(params, { sort: false })}`;
({ body } = await supertest.get(uri).expect(400));
expect(body.message).to.contain(
'[request query.meta_fields]: could not parse array value from json input'
);
});
it('should return 404 the rollup index to query does not exist', async () => {
uri = `${BASE_URI}?${stringify(
{
pattern: 'foo',
params: JSON.stringify({ rollup_index: 'bar' }),
type: 'rollup',
rollup_index: 'bar',
},
{ sort: false }
)}`;
({ body } = await supertest.get(uri).expect(404));
expect(body.message).to.contain('[index_not_found_exception] no such index [bar]');
expect(body.message).to.contain('No indices match pattern "foo"');
});
});
@ -105,7 +59,8 @@ export default function ({ getService }) {
// Query for wildcard
const params = {
pattern: indexName,
params: JSON.stringify({ rollup_index: rollupIndex }),
type: 'rollup',
rollup_index: rollupIndex,
};
const uri = `${BASE_URI}?${stringify(params, { sort: false })}`;
const { body } = await supertest.get(uri).expect(200);