Migrate kibana.autocomplete config to data plugin (#100586) (#101088)

* Migrate kibana.autocomplete config to data plugin

* Fix CI

* Fix tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>
This commit is contained in:
Kibana Machine 2021-06-01 17:34:26 -04:00 committed by GitHub
parent e24af8c236
commit d252c4e794
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 134 additions and 100 deletions

View file

@ -262,11 +262,11 @@ on the {kib} index at startup. {kib} users still need to authenticate with
{es} {ref}/indices-create-index.html[index name limitations].
*Default: `".kibana"`*
| `kibana.autocompleteTimeout:` {ess-icon}
| `data.autocomplete.valueSuggestions.timeout:` {ess-icon}
| Time in milliseconds to wait for autocomplete suggestions from {es}.
This value must be a whole number greater than zero. *Default: `"1000"`*
| `kibana.autocompleteTerminateAfter:` {ess-icon}
| `data.autocomplete.valueSuggestions.terminateAfter:` {ess-icon}
| Maximum number of documents loaded by each shard to generate autocomplete
suggestions. This value must be a whole number greater than zero.
*Default: `"100000"`*

View file

@ -22,8 +22,6 @@ it('set correct defaults ', () => {
const configValue = config.schema.validate({});
expect(configValue).toMatchInlineSnapshot(`
Object {
"autocompleteTerminateAfter": "PT1M40S",
"autocompleteTimeout": "PT1S",
"enabled": true,
"index": ".kibana",
}

View file

@ -29,12 +29,6 @@ export const config = {
schema: schema.object({
enabled: schema.boolean({ defaultValue: true }),
index: schema.string({ defaultValue: '.kibana' }),
autocompleteTerminateAfter: schema.duration({ defaultValue: 100000 }),
autocompleteTimeout: schema.duration({ defaultValue: 1000 }),
}),
deprecations,
exposeToUsage: {
autocompleteTerminateAfter: true,
autocompleteTimeout: true,
},
};

View file

@ -58,8 +58,6 @@ export function pluginInitializerContextConfigMock<T>(config: T) {
const globalConfig: SharedGlobalConfig = {
kibana: {
index: '.kibana-tests',
autocompleteTerminateAfter: duration(100000),
autocompleteTimeout: duration(1000),
},
elasticsearch: {
shardTimeout: duration('30s'),

View file

@ -43,8 +43,6 @@ describe('Legacy config', () => {
expect(legacyConfig).toStrictEqual({
kibana: {
index: '.kibana',
autocompleteTerminateAfter: duration(100000),
autocompleteTimeout: duration(1000),
},
elasticsearch: {
shardTimeout: duration(30, 's'),
@ -66,8 +64,6 @@ describe('Legacy config', () => {
expect(legacyConfig).toStrictEqual({
kibana: {
index: '.kibana',
autocompleteTerminateAfter: duration(100000),
autocompleteTimeout: duration(1000),
},
elasticsearch: {
shardTimeout: duration(30, 's'),

View file

@ -115,8 +115,6 @@ describe('createPluginInitializerContext', () => {
expect(configObject).toStrictEqual({
kibana: {
index: '.kibana',
autocompleteTerminateAfter: duration(100000),
autocompleteTimeout: duration(1000),
},
elasticsearch: {
shardTimeout: duration(30, 's'),

View file

@ -313,7 +313,7 @@ export interface AsyncPlugin<
export const SharedGlobalConfigKeys = {
// We can add more if really needed
kibana: ['index', 'autocompleteTerminateAfter', 'autocompleteTimeout'] as const,
kibana: ['index'] as const,
elasticsearch: ['shardTimeout', 'requestTimeout', 'pingTimeout'] as const,
path: ['data'] as const,
savedObjects: ['maxImportPayloadBytes'] as const,

View file

@ -15,6 +15,8 @@ export const configSchema = schema.object({
}),
valueSuggestions: schema.object({
enabled: schema.boolean({ defaultValue: true }),
terminateAfter: schema.duration({ defaultValue: 100000 }),
timeout: schema.duration({ defaultValue: 1000 }),
}),
}),
search: schema.object({

View file

@ -7,6 +7,7 @@
*/
import { CoreSetup, PluginInitializerContext } from 'src/core/public';
import moment from 'moment';
import { TimefilterSetup } from '../query';
import { QuerySuggestionGetFn } from './providers/query_suggestion_provider';
import {
@ -27,7 +28,7 @@ import { DataPublicPluginStart, DataStartDependencies } from '../types';
export class AutocompleteService {
autocompleteConfig: ConfigSchema['autocomplete'];
constructor(initializerContext: PluginInitializerContext<ConfigSchema>) {
constructor(private initializerContext: PluginInitializerContext<ConfigSchema>) {
const { autocomplete } = initializerContext.config.get<ConfigSchema>();
this.autocompleteConfig = autocomplete;
@ -55,6 +56,8 @@ export class AutocompleteService {
usageCollection,
}: { timefilter: TimefilterSetup; usageCollection?: UsageCollectionSetup }
) {
const { autocomplete } = this.initializerContext.config.get<ConfigSchema>();
const { terminateAfter, timeout } = autocomplete.valueSuggestions;
const usageCollector = createUsageCollector(core.getStartServices, usageCollection);
this.getValueSuggestions = this.autocompleteConfig.valueSuggestions.enabled
@ -71,6 +74,10 @@ export class AutocompleteService {
* please use "getQuerySuggestions" from the start contract
*/
getQuerySuggestions: this.getQuerySuggestions,
getAutocompleteSettings: () => ({
terminateAfter: moment.duration(terminateAfter).asMilliseconds(),
timeout: moment.duration(timeout).asMilliseconds(),
}),
};
}

View file

@ -16,8 +16,9 @@ import { createNowProviderMock } from './now_provider/mocks';
export type Setup = jest.Mocked<ReturnType<Plugin['setup']>>;
export type Start = jest.Mocked<ReturnType<Plugin['start']>>;
const automcompleteSetupMock: jest.Mocked<AutocompleteSetup> = {
const autocompleteSetupMock: jest.Mocked<AutocompleteSetup> = {
getQuerySuggestions: jest.fn(),
getAutocompleteSettings: jest.fn(),
};
const autocompleteStartMock: jest.Mocked<AutocompleteStart> = {
@ -29,7 +30,7 @@ const autocompleteStartMock: jest.Mocked<AutocompleteStart> = {
const createSetupContract = (): Setup => {
const querySetupMock = queryServiceMock.createSetupContract();
return {
autocomplete: automcompleteSetupMock,
autocomplete: autocompleteSetupMock,
search: searchServiceMock.createSetupContract(),
fieldFormats: fieldFormatsServiceMock.createSetupContract(),
query: querySetupMock,

View file

@ -6,23 +6,21 @@
* Side Public License, v 1.
*/
import { TypeOf } from '@kbn/config-schema';
import { CoreSetup, Plugin, PluginInitializerContext } from 'kibana/server';
import { registerRoutes } from './routes';
import { ConfigSchema, configSchema } from '../../config';
import { ConfigSchema } from '../../config';
export class AutocompleteService implements Plugin<void> {
private valueSuggestionsEnabled: boolean = true;
constructor(private initializerContext: PluginInitializerContext<ConfigSchema>) {
initializerContext.config.create<TypeOf<typeof configSchema>>().subscribe((configUpdate) => {
initializerContext.config.create().subscribe((configUpdate) => {
this.valueSuggestionsEnabled = configUpdate.autocomplete.valueSuggestions.enabled;
});
}
public setup(core: CoreSetup) {
if (this.valueSuggestionsEnabled)
registerRoutes(core, this.initializerContext.config.legacy.globalConfig$);
if (this.valueSuggestionsEnabled) registerRoutes(core, this.initializerContext.config.create());
}
public start() {}

View file

@ -7,10 +7,11 @@
*/
import { Observable } from 'rxjs';
import { CoreSetup, SharedGlobalConfig } from 'kibana/server';
import { CoreSetup } from 'kibana/server';
import { registerValueSuggestionsRoute } from './value_suggestions_route';
import { ConfigSchema } from '../../config';
export function registerRoutes({ http }: CoreSetup, config$: Observable<SharedGlobalConfig>): void {
export function registerRoutes({ http }: CoreSetup, config$: Observable<ConfigSchema>): void {
const router = http.createRouter();
registerValueSuggestionsRoute(router, config$);

View file

@ -8,7 +8,7 @@
import { get, map } from 'lodash';
import { schema } from '@kbn/config-schema';
import { IRouter, SharedGlobalConfig } from 'kibana/server';
import { IRouter } from 'kibana/server';
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
@ -16,11 +16,9 @@ import type { estypes } from '@elastic/elasticsearch';
import { IFieldType } from '../index';
import { findIndexPatternById, getFieldByName } from '../index_patterns';
import { getRequestAbortedSignal } from '../lib';
import { ConfigSchema } from '../../config';
export function registerValueSuggestionsRoute(
router: IRouter,
config$: Observable<SharedGlobalConfig>
) {
export function registerValueSuggestionsRoute(router: IRouter, config$: Observable<ConfigSchema>) {
router.post(
{
path: '/api/kibana/suggestions/values/{index}',
@ -50,8 +48,8 @@ export function registerValueSuggestionsRoute(
const signal = getRequestAbortedSignal(request.events.aborted$);
const autocompleteSearchOptions = {
timeout: `${config.kibana.autocompleteTimeout.asMilliseconds()}ms`,
terminate_after: config.kibana.autocompleteTerminateAfter.asMilliseconds(),
timeout: `${config.autocomplete.valueSuggestions.timeout.asMilliseconds()}ms`,
terminate_after: config.autocomplete.valueSuggestions.terminateAfter.asMilliseconds(),
};
let field: IFieldType | undefined = fieldMeta;

View file

@ -0,0 +1,71 @@
/*
* 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 { cloneDeep } from 'lodash';
import { applyDeprecations, configDeprecationFactory } from '@kbn/config';
import { autocompleteConfigDeprecationProvider } from './config_deprecations';
const applyConfigDeprecations = (settings: Record<string, any> = {}) => {
const deprecations = autocompleteConfigDeprecationProvider(configDeprecationFactory);
const deprecationMessages: string[] = [];
const migrated = applyDeprecations(
settings,
deprecations.map((deprecation) => ({
deprecation,
path: '',
})),
() => ({ message }) => deprecationMessages.push(message)
);
return {
messages: deprecationMessages,
migrated: migrated.config,
};
};
describe('Config Deprecations', () => {
it('does not report deprecations for default configuration', () => {
const defaultConfig = { data: { autocomplete: { valueSuggestions: {} } } };
const { messages, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig));
expect(migrated).toEqual(defaultConfig);
expect(messages).toHaveLength(0);
});
it('renames kibana.autocompleteTerminateAfter to data.autocomplete.valueSuggestions.terminateAfter', () => {
const config = {
kibana: {
autocompleteTerminateAfter: 123,
},
};
const { messages, migrated } = applyConfigDeprecations(cloneDeep(config));
expect(migrated.kibana.autocompleteTerminateAfter).not.toBeDefined();
expect(migrated.data.autocomplete.valueSuggestions.terminateAfter).toEqual(123);
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"kibana.autocompleteTerminateAfter\\" is deprecated and has been replaced by \\"data.autocomplete.valueSuggestions.terminateAfter\\"",
]
`);
});
it('renames kibana.autocompleteTimeout to data.autocomplete.valueSuggestions.timeout', () => {
const config = {
kibana: {
autocompleteTimeout: 123,
},
};
const { messages, migrated } = applyConfigDeprecations(cloneDeep(config));
expect(migrated.kibana.autocompleteTimeout).not.toBeDefined();
expect(migrated.data.autocomplete.valueSuggestions.timeout).toEqual(123);
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"kibana.autocompleteTimeout\\" is deprecated and has been replaced by \\"data.autocomplete.valueSuggestions.timeout\\"",
]
`);
});
});

View file

@ -0,0 +1,19 @@
/*
* 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 type { ConfigDeprecationProvider } from 'src/core/server';
export const autocompleteConfigDeprecationProvider: ConfigDeprecationProvider = ({
renameFromRoot,
}) => [
renameFromRoot(
'kibana.autocompleteTerminateAfter',
'data.autocomplete.valueSuggestions.terminateAfter'
),
renameFromRoot('kibana.autocompleteTimeout', 'data.autocomplete.valueSuggestions.timeout'),
];

View file

@ -181,6 +181,7 @@ import {
tabifyGetColumns,
calcAutoIntervalLessThan,
} from '../common';
import { autocompleteConfigDeprecationProvider } from './config_deprecations';
export {
// aggs
@ -301,6 +302,7 @@ export {
};
export const config: PluginConfigDescriptor<ConfigSchema> = {
deprecations: autocompleteConfigDeprecationProvider,
exposeToBrowser: {
autocomplete: true,
search: true,

View file

@ -1549,18 +1549,18 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isNestedField" 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:245:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:261:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:266:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:269:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:258:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/plugin.ts:81:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/search/types.ts:115:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts

View file

@ -2,7 +2,7 @@
"id": "inputControlVis",
"version": "8.0.0",
"kibanaVersion": "kibana",
"server": true,
"server": false,
"ui": true,
"requiredPlugins": [
"data",

View file

@ -44,8 +44,6 @@ export interface InputControlVisPluginStartDependencies {
/** @internal */
export class InputControlVisPlugin implements Plugin<void, void> {
private cachedSettings: InputControlSettings | undefined = undefined;
constructor(public initializerContext: PluginInitializerContext) {}
public setup(
@ -56,13 +54,8 @@ export class InputControlVisPlugin implements Plugin<void, void> {
core,
data,
getSettings: async () => {
if (!this.cachedSettings) {
this.cachedSettings = await core.http.get<InputControlSettings>(
'/api/input_control_vis/settings'
);
}
return this.cachedSettings;
const { timeout, terminateAfter } = data.autocomplete.getAutocompleteSettings();
return { autocompleteTimeout: timeout, autocompleteTerminateAfter: terminateAfter };
},
};

View file

@ -1,42 +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
* 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 { CoreSetup, PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server';
import { schema } from '@kbn/config-schema';
import { first } from 'rxjs/operators';
export const config: PluginConfigDescriptor = {
schema: schema.object({ enabled: schema.boolean({ defaultValue: true }) }),
};
export const plugin = (initializerContext: PluginInitializerContext) => ({
setup(core: CoreSetup) {
// TODO this is a workaround to pass global config settings to the client
// once kibana.autocompleteTerminateAfter and kibana.autocompleteTimeout
// are migrated completely and owned by a plugin, this can be done completely
// client side and the additional endpoint is not required anymore
core.http.createRouter().get(
{
path: '/api/input_control_vis/settings',
validate: false,
},
async (context, request, response) => {
const legacyConfig = await initializerContext.config.legacy.globalConfig$
.pipe(first())
.toPromise();
return response.ok({
body: {
autocompleteTimeout: legacyConfig.kibana.autocompleteTimeout.asMilliseconds(),
autocompleteTerminateAfter: legacyConfig.kibana.autocompleteTerminateAfter.asMilliseconds(),
},
});
}
);
},
start() {},
});