From 581f1bd6e9329b66db5bc3cb3348475595db5aa2 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 24 Feb 2020 16:26:34 +0100 Subject: [PATCH 01/21] Expressions debug mode (#57841) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add ability to execute expression in debug mode * feat: 🎸 store resolved arguments in debug information * feat: 🎸 track function execution time and set "success" flag * feat: 🎸 store debug information for functions that throw * feat: 🎸 use performance.now, safe "fn" reference, fix typo --- src/plugins/expressions/common/ast/types.ts | 53 ++++ .../common/execution/execution.test.ts | 253 +++++++++++++++++- .../expressions/common/execution/execution.ts | 67 ++++- .../expressions/common/executor/executor.ts | 26 +- 4 files changed, 379 insertions(+), 20 deletions(-) diff --git a/src/plugins/expressions/common/ast/types.ts b/src/plugins/expressions/common/ast/types.ts index 82a7578dd4b892..0b505f117a5807 100644 --- a/src/plugins/expressions/common/ast/types.ts +++ b/src/plugins/expressions/common/ast/types.ts @@ -17,6 +17,9 @@ * under the License. */ +import { ExpressionValue, ExpressionValueError } from '../expression_types'; +import { ExpressionFunction } from '../../public'; + export type ExpressionAstNode = | ExpressionAstExpression | ExpressionAstFunction @@ -31,6 +34,56 @@ export interface ExpressionAstFunction { type: 'function'; function: string; arguments: Record; + + /** + * Debug information added to each function when expression is executed in *debug mode*. + */ + debug?: ExpressionAstFunctionDebug; +} + +export interface ExpressionAstFunctionDebug { + /** + * True if function successfully returned output, false if function threw. + */ + success: boolean; + + /** + * Reference to the expression function this AST node represents. + */ + fn: ExpressionFunction; + + /** + * Input that expression function received as its first argument. + */ + input: ExpressionValue; + + /** + * Map of resolved arguments expression function received as its second argument. + */ + args: Record; + + /** + * Result returned by the expression function. Including an error result + * if it was returned by the function (not thrown). + */ + output?: ExpressionValue; + + /** + * Error that function threw normalized to `ExpressionValueError`. + */ + error?: ExpressionValueError; + + /** + * Raw error that was thrown by the function, if any. + */ + rawError?: any | Error; + + /** + * Time in milliseconds it took to execute the function. Duration can be + * `undefined` if error happened during argument resolution, because function + * timing starts after the arguments have been resolved. + */ + duration: number | undefined; } export type ExpressionAstArgument = string | boolean | number | ExpressionAstExpression; diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index b6c1721e33eefc..f6ff9efca848b9 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -18,20 +18,28 @@ */ import { Execution } from './execution'; -import { parseExpression } from '../ast'; +import { parseExpression, ExpressionAstExpression } from '../ast'; import { createUnitTestExecutor } from '../test_helpers'; import { ExpressionFunctionDefinition } from '../../public'; import { ExecutionContract } from './execution_contract'; +beforeAll(() => { + if (typeof performance === 'undefined') { + (global as any).performance = { now: Date.now }; + } +}); + const createExecution = ( expression: string = 'foo bar=123', - context: Record = {} + context: Record = {}, + debug: boolean = false ) => { const executor = createUnitTestExecutor(); const execution = new Execution({ executor, ast: parseExpression(expression), context, + debug, }); return execution; }; @@ -406,4 +414,245 @@ describe('Execution', () => { }); }); }); + + describe('debug mode', () => { + test('can execute expression in debug mode', async () => { + const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true); + execution.start(-1); + const result = await execution.result; + + expect(result).toEqual({ + type: 'num', + value: 5, + }); + }); + + test('can execute expression with sub-expression in debug mode', async () => { + const execution = createExecution( + 'add val={var_set name=foo value=5 | var name=foo} | add val=10', + {}, + true + ); + execution.start(0); + const result = await execution.result; + + expect(result).toEqual({ + type: 'num', + value: 15, + }); + }); + + describe('when functions succeed', () => { + test('sets "success" flag on all functions to true', async () => { + const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true); + execution.start(-1); + await execution.result; + + for (const node of execution.state.get().ast.chain) { + expect(node.debug?.success).toBe(true); + } + }); + + test('stores "fn" reference to the function', async () => { + const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true); + execution.start(-1); + await execution.result; + + for (const node of execution.state.get().ast.chain) { + expect(node.debug?.fn.name).toBe('add'); + } + }); + + test('saves duration it took to execute each function', async () => { + const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true); + execution.start(-1); + await execution.result; + + for (const node of execution.state.get().ast.chain) { + expect(typeof node.debug?.duration).toBe('number'); + expect(node.debug?.duration).toBeLessThan(100); + expect(node.debug?.duration).toBeGreaterThanOrEqual(0); + } + }); + + test('sets duration to 10 milliseconds when function executes 10 milliseconds', async () => { + const execution = createExecution('sleep 10', {}, true); + execution.start(-1); + await execution.result; + + const node = execution.state.get().ast.chain[0]; + expect(typeof node.debug?.duration).toBe('number'); + expect(node.debug?.duration).toBeLessThan(50); + expect(node.debug?.duration).toBeGreaterThanOrEqual(5); + }); + + test('adds .debug field in expression AST on each executed function', async () => { + const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true); + execution.start(-1); + await execution.result; + + for (const node of execution.state.get().ast.chain) { + expect(typeof node.debug).toBe('object'); + expect(!!node.debug).toBe(true); + } + }); + + test('stores input of each function', async () => { + const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true); + execution.start(-1); + await execution.result; + + const { chain } = execution.state.get().ast; + + expect(chain[0].debug!.input).toBe(-1); + expect(chain[1].debug!.input).toEqual({ + type: 'num', + value: 0, + }); + expect(chain[2].debug!.input).toEqual({ + type: 'num', + value: 2, + }); + }); + + test('stores output of each function', async () => { + const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true); + execution.start(-1); + await execution.result; + + const { chain } = execution.state.get().ast; + + expect(chain[0].debug!.output).toEqual({ + type: 'num', + value: 0, + }); + expect(chain[1].debug!.output).toEqual({ + type: 'num', + value: 2, + }); + expect(chain[2].debug!.output).toEqual({ + type: 'num', + value: 5, + }); + }); + + test('stores resolved arguments of a function', async () => { + const execution = createExecution( + 'add val={var_set name=foo value=5 | var name=foo} | add val=10', + {}, + true + ); + execution.start(-1); + await execution.result; + + const { chain } = execution.state.get().ast; + + expect(chain[0].debug!.args).toEqual({ + val: 5, + }); + + expect((chain[0].arguments.val[0] as ExpressionAstExpression).chain[0].debug!.args).toEqual( + { + name: 'foo', + value: 5, + } + ); + }); + + test('store debug information about sub-expressions', async () => { + const execution = createExecution( + 'add val={var_set name=foo value=5 | var name=foo} | add val=10', + {}, + true + ); + execution.start(0); + await execution.result; + + const { chain } = execution.state.get().ast.chain[0].arguments + .val[0] as ExpressionAstExpression; + + expect(typeof chain[0].debug).toBe('object'); + expect(typeof chain[1].debug).toBe('object'); + expect(!!chain[0].debug).toBe(true); + expect(!!chain[1].debug).toBe(true); + + expect(chain[0].debug!.input).toBe(0); + expect(chain[0].debug!.output).toBe(0); + expect(chain[1].debug!.input).toBe(0); + expect(chain[1].debug!.output).toBe(5); + }); + }); + + describe('when expression throws', () => { + const executor = createUnitTestExecutor(); + executor.registerFunction({ + name: 'throws', + args: {}, + help: '', + fn: () => { + throw new Error('foo'); + }, + }); + + test('stores debug information up until the function that throws', async () => { + const execution = new Execution({ + executor, + ast: parseExpression('add val=1 | throws | add val=3'), + debug: true, + }); + execution.start(0); + await execution.result; + + const node1 = execution.state.get().ast.chain[0]; + const node2 = execution.state.get().ast.chain[1]; + const node3 = execution.state.get().ast.chain[2]; + + expect(typeof node1.debug).toBe('object'); + expect(typeof node2.debug).toBe('object'); + expect(typeof node3.debug).toBe('undefined'); + }); + + test('stores error thrown in debug information', async () => { + const execution = new Execution({ + executor, + ast: parseExpression('add val=1 | throws | add val=3'), + debug: true, + }); + execution.start(0); + await execution.result; + + const node2 = execution.state.get().ast.chain[1]; + + expect(node2.debug?.error).toMatchObject({ + type: 'error', + error: { + message: '[throws] > foo', + }, + }); + expect(node2.debug?.rawError).toBeInstanceOf(Error); + }); + + test('sets .debug object to expected shape', async () => { + const execution = new Execution({ + executor, + ast: parseExpression('add val=1 | throws | add val=3'), + debug: true, + }); + execution.start(0); + await execution.result; + + const node2 = execution.state.get().ast.chain[1]; + + expect(node2.debug).toMatchObject({ + success: false, + fn: expect.any(Object), + input: expect.any(Object), + args: expect.any(Object), + error: expect.any(Object), + rawError: expect.any(Error), + duration: expect.any(Number), + }); + }); + }); + }); }); diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 2a272e187cffcf..7e7df822724aea 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -23,7 +23,7 @@ import { createExecutionContainer, ExecutionContainer } from './container'; import { createError } from '../util'; import { Defer } from '../../../kibana_utils/common'; import { RequestAdapter, DataAdapter } from '../../../inspector/common'; -import { isExpressionValueError } from '../expression_types/specs/error'; +import { isExpressionValueError, ExpressionValueError } from '../expression_types/specs/error'; import { ExpressionAstExpression, ExpressionAstFunction, @@ -32,7 +32,7 @@ import { parseExpression, } from '../ast'; import { ExecutionContext, DefaultInspectorAdapters } from './types'; -import { getType } from '../expression_types'; +import { getType, ExpressionValue } from '../expression_types'; import { ArgumentType, ExpressionFunction } from '../expression_functions'; import { getByAlias } from '../util/get_by_alias'; import { ExecutionContract } from './execution_contract'; @@ -44,6 +44,13 @@ export interface ExecutionParams< ast?: ExpressionAstExpression; expression?: string; context?: ExtraContext; + + /** + * Whether to execute expression in *debug mode*. In *debug mode* inputs and + * outputs as well as all resolved arguments and time it took to execute each + * function are saved and are available for introspection. + */ + debug?: boolean; } const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({ @@ -190,23 +197,55 @@ export class Execution< } const { function: fnName, arguments: fnArgs } = link; - const fnDef = getByAlias(this.state.get().functions, fnName); + const fn = getByAlias(this.state.get().functions, fnName); - if (!fnDef) { + if (!fn) { return createError({ message: `Function ${fnName} could not be found.` }); } + let args: Record = {}; + let timeStart: number | undefined; + try { - // Resolve arguments before passing to function - // resolveArgs returns an object because the arguments themselves might - // actually have a 'then' function which would be treated as a promise - const { resolvedArgs } = await this.resolveArgs(fnDef, input, fnArgs); - const output = await this.invokeFunction(fnDef, input, resolvedArgs); + // `resolveArgs` returns an object because the arguments themselves might + // actually have a `then` function which would be treated as a `Promise`. + const { resolvedArgs } = await this.resolveArgs(fn, input, fnArgs); + args = resolvedArgs; + timeStart = this.params.debug ? performance.now() : 0; + const output = await this.invokeFunction(fn, input, resolvedArgs); + + if (this.params.debug) { + const timeEnd: number = performance.now(); + (link as ExpressionAstFunction).debug = { + success: true, + fn, + input, + args: resolvedArgs, + output, + duration: timeEnd - timeStart, + }; + } + if (getType(output) === 'error') return output; input = output; - } catch (e) { - e.message = `[${fnName}] > ${e.message}`; - return createError(e); + } catch (rawError) { + const timeEnd: number = this.params.debug ? performance.now() : 0; + rawError.message = `[${fnName}] > ${rawError.message}`; + const error = createError(rawError) as ExpressionValueError; + + if (this.params.debug) { + (link as ExpressionAstFunction).debug = { + success: false, + fn, + input, + args, + error, + rawError, + duration: timeStart ? timeEnd - timeStart : undefined, + }; + } + + return error; } } @@ -327,7 +366,9 @@ export class Execution< const resolveArgFns = mapValues(argAstsWithDefaults, (asts, argName) => { return asts.map((item: ExpressionAstExpression) => { return async (subInput = input) => { - const output = await this.params.executor.interpret(item, subInput); + const output = await this.params.executor.interpret(item, subInput, { + debug: this.params.debug, + }); if (isExpressionValueError(output)) throw output.error; const casted = this.cast(output, argDefs[argName as any].types); return casted; diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index af3662d13de4ef..2ecbc5f75a9e87 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -31,6 +31,15 @@ import { ExpressionAstExpression, ExpressionAstNode } from '../ast'; import { typeSpecs } from '../expression_types/specs'; import { functionSpecs } from '../expression_functions/specs'; +export interface ExpressionExecOptions { + /** + * Whether to execute expression in *debug mode*. In *debug mode* inputs and + * outputs as well as all resolved arguments and time it took to execute each + * function are saved and are available for introspection. + */ + debug?: boolean; +} + export class TypesRegistry implements IRegistry { constructor(private readonly executor: Executor) {} @@ -145,10 +154,14 @@ export class Executor = Record(ast: ExpressionAstNode, input: T): Promise { + public async interpret( + ast: ExpressionAstNode, + input: T, + options?: ExpressionExecOptions + ): Promise { switch (getType(ast)) { case 'expression': - return await this.interpretExpression(ast as ExpressionAstExpression, input); + return await this.interpretExpression(ast as ExpressionAstExpression, input, options); case 'string': case 'number': case 'null': @@ -161,9 +174,10 @@ export class Executor = Record( ast: string | ExpressionAstExpression, - input: T + input: T, + options?: ExpressionExecOptions ): Promise { - const execution = this.createExecution(ast); + const execution = this.createExecution(ast, undefined, options); execution.start(input); return await execution.result; } @@ -192,7 +206,8 @@ export class Executor = Record( ast: string | ExpressionAstExpression, - context: ExtraContext = {} as ExtraContext + context: ExtraContext = {} as ExtraContext, + { debug }: ExpressionExecOptions = {} as ExpressionExecOptions ): Execution { const params: ExecutionParams = { executor: this, @@ -200,6 +215,7 @@ export class Executor = Record Date: Mon, 24 Feb 2020 16:40:20 +0100 Subject: [PATCH 02/21] no sparse array by default. (#58212) Co-authored-by: Elastic Machine --- .../src/types/array_type.test.ts | 37 ++++++++++++++++--- .../kbn-config-schema/src/types/array_type.ts | 4 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/kbn-config-schema/src/types/array_type.test.ts b/packages/kbn-config-schema/src/types/array_type.test.ts index 73661ef849cf4f..66b72096a593da 100644 --- a/packages/kbn-config-schema/src/types/array_type.test.ts +++ b/packages/kbn-config-schema/src/types/array_type.test.ts @@ -85,14 +85,29 @@ test('fails if mixed types of content in array', () => { ); }); -test('returns empty array if input is empty but type has default value', () => { - const type = schema.arrayOf(schema.string({ defaultValue: 'test' })); +test('fails if sparse content in array', () => { + const type = schema.arrayOf(schema.string()); expect(type.validate([])).toEqual([]); + expect(() => type.validate([undefined])).toThrowErrorMatchingInlineSnapshot( + `"[0]: sparse array are not allowed"` + ); }); -test('returns empty array if input is empty even if type is required', () => { - const type = schema.arrayOf(schema.string()); +test('fails if sparse content in array if optional', () => { + const type = schema.arrayOf(schema.maybe(schema.string())); + expect(type.validate([])).toEqual([]); + expect(() => type.validate([undefined])).toThrowErrorMatchingInlineSnapshot( + `"[0]: sparse array are not allowed"` + ); +}); + +test('fails if sparse content in array if nullable', () => { + const type = schema.arrayOf(schema.nullable(schema.string())); expect(type.validate([])).toEqual([]); + expect(type.validate([null])).toEqual([null]); + expect(() => type.validate([undefined])).toThrowErrorMatchingInlineSnapshot( + `"[0]: sparse array are not allowed"` + ); }); test('fails for null values if optional', () => { @@ -102,9 +117,19 @@ test('fails for null values if optional', () => { ); }); +test('returns empty array if input is empty but type has default value', () => { + const type = schema.arrayOf(schema.string({ defaultValue: 'test' })); + expect(type.validate([])).toEqual([]); +}); + +test('returns empty array if input is empty even if type is required', () => { + const type = schema.arrayOf(schema.string()); + expect(type.validate([])).toEqual([]); +}); + test('handles default values for undefined values', () => { - const type = schema.arrayOf(schema.string({ defaultValue: 'foo' })); - expect(type.validate([undefined])).toEqual(['foo']); + const type = schema.arrayOf(schema.string(), { defaultValue: ['foo'] }); + expect(type.validate(undefined)).toEqual(['foo']); }); test('array within array', () => { diff --git a/packages/kbn-config-schema/src/types/array_type.ts b/packages/kbn-config-schema/src/types/array_type.ts index ad74f375588ad7..a0353e8348ddd6 100644 --- a/packages/kbn-config-schema/src/types/array_type.ts +++ b/packages/kbn-config-schema/src/types/array_type.ts @@ -31,7 +31,7 @@ export class ArrayType extends Type { let schema = internals .array() .items(type.getSchema().optional()) - .sparse(); + .sparse(false); if (options.minSize !== undefined) { schema = schema.min(options.minSize); @@ -49,6 +49,8 @@ export class ArrayType extends Type { case 'any.required': case 'array.base': return `expected value of type [array] but got [${typeDetect(value)}]`; + case 'array.sparse': + return `sparse array are not allowed`; case 'array.parse': return `could not parse array value from [${value}]`; case 'array.min': From 6b735c9ca016a65cc0c2e29fb144a1046020f1d3 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Mon, 24 Feb 2020 11:17:29 -0500 Subject: [PATCH 03/21] [ML] New Platform server shim: update usage collector to use core savedObjects (#58058) * use NP savedObjects and createInternalRepository for usage collection * fix route doc typo * update MlTelemetrySavedObject type * remove fileDataVisualizer routes dependency on legacy es plugin * update mlTelemetry tests * remove deprecated use of getSavedObjectsClient --- x-pack/legacy/plugins/ml/index.ts | 3 +- .../ml/server/lib/ml_telemetry/index.ts | 1 - .../ml_telemetry/make_ml_usage_collector.ts | 15 ++- .../lib/ml_telemetry/ml_telemetry.test.ts | 102 +++++------------- .../server/lib/ml_telemetry/ml_telemetry.ts | 39 +++---- .../plugins/ml/server/new_platform/plugin.ts | 21 ++-- .../ml/server/routes/file_data_visualizer.ts | 2 +- .../ml/server/routes/job_audit_messages.ts | 2 +- 8 files changed, 55 insertions(+), 130 deletions(-) diff --git a/x-pack/legacy/plugins/ml/index.ts b/x-pack/legacy/plugins/ml/index.ts index 0ef5e14e44f718..09f1b9ccedce4f 100755 --- a/x-pack/legacy/plugins/ml/index.ts +++ b/x-pack/legacy/plugins/ml/index.ts @@ -81,7 +81,8 @@ export const ml = (kibana: any) => { injectUiAppVars: server.injectUiAppVars, http: mlHttpService, savedObjects: server.savedObjects, - elasticsearch: kbnServer.newPlatform.setup.core.elasticsearch, // NP + coreSavedObjects: kbnServer.newPlatform.start.core.savedObjects, + elasticsearch: kbnServer.newPlatform.setup.core.elasticsearch, }; const { usageCollection, cloud, home } = kbnServer.newPlatform.setup.plugins; const plugins = { diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts index 5da4f6b62bcec9..dffd95f50e0d95 100644 --- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts +++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts @@ -6,7 +6,6 @@ export { createMlTelemetry, - getSavedObjectsClient, incrementFileDataVisualizerIndexCreationCount, storeMlTelemetry, MlTelemetry, diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts index 293480b2aa5dc8..a120450bbb2b0a 100644 --- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts +++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts @@ -5,19 +5,17 @@ */ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { SavedObjectsServiceStart } from 'src/core/server'; import { createMlTelemetry, - getSavedObjectsClient, ML_TELEMETRY_DOC_ID, MlTelemetry, MlTelemetrySavedObject, } from './ml_telemetry'; -import { UsageInitialization } from '../../new_platform/plugin'; - export function makeMlUsageCollector( usageCollection: UsageCollectionSetup | undefined, - { elasticsearchPlugin, savedObjects }: UsageInitialization + savedObjects: SavedObjectsServiceStart ): void { if (!usageCollection) { return; @@ -28,11 +26,10 @@ export function makeMlUsageCollector( isReady: () => true, fetch: async (): Promise => { try { - const savedObjectsClient = getSavedObjectsClient(elasticsearchPlugin, savedObjects); - const mlTelemetrySavedObject = (await savedObjectsClient.get( - 'ml-telemetry', - ML_TELEMETRY_DOC_ID - )) as MlTelemetrySavedObject; + const mlTelemetrySavedObject: MlTelemetrySavedObject = await savedObjects + .createInternalRepository() + .get('ml-telemetry', ML_TELEMETRY_DOC_ID); + return mlTelemetrySavedObject.attributes; } catch (err) { return createMlTelemetry(); diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts index fcf3763626b6f6..9d14ffb31be631 100644 --- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts +++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts @@ -6,7 +6,6 @@ import { createMlTelemetry, - getSavedObjectsClient, incrementFileDataVisualizerIndexCreationCount, ML_TELEMETRY_DOC_ID, MlTelemetry, @@ -26,22 +25,11 @@ describe('ml_telemetry', () => { }); describe('storeMlTelemetry', () => { - let elasticsearchPlugin: any; - let savedObjects: any; let mlTelemetry: MlTelemetry; - let savedObjectsClientInstance: any; + let internalRepository: any; beforeEach(() => { - savedObjectsClientInstance = { create: jest.fn() }; - const callWithInternalUser = jest.fn(); - const internalRepository = jest.fn(); - elasticsearchPlugin = { - getCluster: jest.fn(() => ({ callWithInternalUser })), - }; - savedObjects = { - SavedObjectsClient: jest.fn(() => savedObjectsClientInstance), - getSavedObjectsRepository: jest.fn(() => internalRepository), - }; + internalRepository = { create: jest.fn(), get: jest.fn() }; mlTelemetry = { file_data_visualizer: { index_creation_count: 1, @@ -49,59 +37,28 @@ describe('ml_telemetry', () => { }; }); - it('should call savedObjectsClient create with the given MlTelemetry object', () => { - storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry); - expect(savedObjectsClientInstance.create.mock.calls[0][1]).toBe(mlTelemetry); + it('should call internalRepository create with the given MlTelemetry object', () => { + storeMlTelemetry(internalRepository, mlTelemetry); + expect(internalRepository.create.mock.calls[0][1]).toBe(mlTelemetry); }); - it('should call savedObjectsClient create with the ml-telemetry document type and ID', () => { - storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry); - expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe('ml-telemetry'); - expect(savedObjectsClientInstance.create.mock.calls[0][2].id).toBe(ML_TELEMETRY_DOC_ID); + it('should call internalRepository create with the ml-telemetry document type and ID', () => { + storeMlTelemetry(internalRepository, mlTelemetry); + expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry'); + expect(internalRepository.create.mock.calls[0][2].id).toBe(ML_TELEMETRY_DOC_ID); }); - it('should call savedObjectsClient create with overwrite: true', () => { - storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry); - expect(savedObjectsClientInstance.create.mock.calls[0][2].overwrite).toBe(true); - }); - }); - - describe('getSavedObjectsClient', () => { - let elasticsearchPlugin: any; - let savedObjects: any; - let savedObjectsClientInstance: any; - let callWithInternalUser: any; - let internalRepository: any; - - beforeEach(() => { - savedObjectsClientInstance = { create: jest.fn() }; - callWithInternalUser = jest.fn(); - internalRepository = jest.fn(); - elasticsearchPlugin = { - getCluster: jest.fn(() => ({ callWithInternalUser })), - }; - savedObjects = { - SavedObjectsClient: jest.fn(() => savedObjectsClientInstance), - getSavedObjectsRepository: jest.fn(() => internalRepository), - }; - }); - - it('should return a SavedObjectsClient initialized with the saved objects internal repository', () => { - const result = getSavedObjectsClient(elasticsearchPlugin, savedObjects); - - expect(result).toBe(savedObjectsClientInstance); - expect(savedObjects.SavedObjectsClient).toHaveBeenCalledWith(internalRepository); + it('should call internalRepository create with overwrite: true', () => { + storeMlTelemetry(internalRepository, mlTelemetry); + expect(internalRepository.create.mock.calls[0][2].overwrite).toBe(true); }); }); describe('incrementFileDataVisualizerIndexCreationCount', () => { - let elasticsearchPlugin: any; let savedObjects: any; - let savedObjectsClientInstance: any; - let callWithInternalUser: any; let internalRepository: any; - function createSavedObjectsClientInstance( + function createInternalRepositoryInstance( telemetryEnabled?: boolean, indexCreationCount?: number ) { @@ -136,51 +93,42 @@ describe('ml_telemetry', () => { } function mockInit(telemetryEnabled?: boolean, indexCreationCount?: number): void { - savedObjectsClientInstance = createSavedObjectsClientInstance( - telemetryEnabled, - indexCreationCount - ); - callWithInternalUser = jest.fn(); - internalRepository = jest.fn(); + internalRepository = createInternalRepositoryInstance(telemetryEnabled, indexCreationCount); savedObjects = { - SavedObjectsClient: jest.fn(() => savedObjectsClientInstance), - getSavedObjectsRepository: jest.fn(() => internalRepository), - }; - elasticsearchPlugin = { - getCluster: jest.fn(() => ({ callWithInternalUser })), + createInternalRepository: jest.fn(() => internalRepository), }; } it('should not increment if telemetry status cannot be determined', async () => { mockInit(); - await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects); + await incrementFileDataVisualizerIndexCreationCount(savedObjects); - expect(savedObjectsClientInstance.create.mock.calls).toHaveLength(0); + expect(internalRepository.create.mock.calls).toHaveLength(0); }); it('should not increment if telemetry status is disabled', async () => { mockInit(false); - await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects); + await incrementFileDataVisualizerIndexCreationCount(savedObjects); - expect(savedObjectsClientInstance.create.mock.calls).toHaveLength(0); + expect(internalRepository.create.mock.calls).toHaveLength(0); }); it('should initialize index_creation_count with 1', async () => { mockInit(true); - await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects); + await incrementFileDataVisualizerIndexCreationCount(savedObjects); - expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe('ml-telemetry'); - expect(savedObjectsClientInstance.create.mock.calls[0][1]).toEqual({ + expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry'); + expect(internalRepository.create.mock.calls[0][1]).toEqual({ file_data_visualizer: { index_creation_count: 1 }, }); }); it('should increment index_creation_count to 2', async () => { mockInit(true, 1); - await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects); + await incrementFileDataVisualizerIndexCreationCount(savedObjects); - expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe('ml-telemetry'); - expect(savedObjectsClientInstance.create.mock.calls[0][1]).toEqual({ + expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry'); + expect(internalRepository.create.mock.calls[0][1]).toEqual({ file_data_visualizer: { index_creation_count: 2 }, }); }); diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts index 1bac3f17806449..d76b1ee94e21e9 100644 --- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts +++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch'; -import { SavedObjectsLegacyService } from 'src/legacy/server/kbn_server'; -import { callWithInternalUserFactory } from '../../client/call_with_internal_user_factory'; +import { + SavedObjectAttributes, + SavedObjectsServiceStart, + ISavedObjectsRepository, +} from 'src/core/server'; -export interface MlTelemetry { +export interface MlTelemetry extends SavedObjectAttributes { file_data_visualizer: { index_creation_count: number; }; @@ -29,35 +31,22 @@ export function createMlTelemetry(count: number = 0): MlTelemetry { } // savedObjects export function storeMlTelemetry( - elasticsearchPlugin: ElasticsearchPlugin, - savedObjects: SavedObjectsLegacyService, + internalRepository: ISavedObjectsRepository, mlTelemetry: MlTelemetry ): void { - const savedObjectsClient = getSavedObjectsClient(elasticsearchPlugin, savedObjects); - savedObjectsClient.create('ml-telemetry', mlTelemetry, { + internalRepository.create('ml-telemetry', mlTelemetry, { id: ML_TELEMETRY_DOC_ID, overwrite: true, }); } -// needs savedObjects and elasticsearchPlugin -export function getSavedObjectsClient( - elasticsearchPlugin: ElasticsearchPlugin, - savedObjects: SavedObjectsLegacyService -): any { - const { SavedObjectsClient, getSavedObjectsRepository } = savedObjects; - const callWithInternalUser = callWithInternalUserFactory(elasticsearchPlugin); - const internalRepository = getSavedObjectsRepository(callWithInternalUser); - return new SavedObjectsClient(internalRepository); -} export async function incrementFileDataVisualizerIndexCreationCount( - elasticsearchPlugin: ElasticsearchPlugin, - savedObjects: SavedObjectsLegacyService + savedObjects: SavedObjectsServiceStart ): Promise { - const savedObjectsClient = getSavedObjectsClient(elasticsearchPlugin, savedObjects); - + const internalRepository = await savedObjects.createInternalRepository(); try { - const { attributes } = await savedObjectsClient.get('telemetry', 'telemetry'); + const { attributes } = await internalRepository.get('telemetry', 'telemetry'); + if (attributes.enabled === false) { return; } @@ -70,7 +59,7 @@ export async function incrementFileDataVisualizerIndexCreationCount( let indicesCount = 1; try { - const { attributes } = (await savedObjectsClient.get( + const { attributes } = (await internalRepository.get( 'ml-telemetry', ML_TELEMETRY_DOC_ID )) as MlTelemetrySavedObject; @@ -80,5 +69,5 @@ export async function incrementFileDataVisualizerIndexCreationCount( } const mlTelemetry = createMlTelemetry(indicesCount); - storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry); + storeMlTelemetry(internalRepository, mlTelemetry); } diff --git a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts index 10961182be841a..43c276ac63a13d 100644 --- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts +++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts @@ -14,6 +14,7 @@ import { CoreSetup, IRouter, IScopedClusterClient, + SavedObjectsServiceStart, } from 'src/core/server'; import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; @@ -28,12 +29,10 @@ import { LICENSE_TYPE } from '../../common/constants/license'; import { annotationRoutes } from '../routes/annotations'; import { jobRoutes } from '../routes/anomaly_detectors'; import { dataFeedRoutes } from '../routes/datafeeds'; -// @ts-ignore: could not find declaration file for module import { indicesRoutes } from '../routes/indices'; import { jobValidationRoutes } from '../routes/job_validation'; import { makeMlUsageCollector } from '../lib/ml_telemetry'; import { notificationRoutes } from '../routes/notification_settings'; -// @ts-ignore: could not find declaration file for module import { systemRoutes } from '../routes/system'; import { dataFrameAnalyticsRoutes } from '../routes/data_frame_analytics'; import { dataRecognizer } from '../routes/modules'; @@ -45,7 +44,6 @@ import { filtersRoutes } from '../routes/filters'; import { resultsServiceRoutes } from '../routes/results_service'; import { jobServiceRoutes } from '../routes/job_service'; import { jobAuditMessagesRoutes } from '../routes/job_audit_messages'; -// @ts-ignore: could not find declaration file for module import { fileDataVisualizerRoutes } from '../routes/file_data_visualizer'; import { initMlServerLog, LogInitialization } from '../client/log'; import { HomeServerPluginSetup } from '../../../../../../src/plugins/home/server'; @@ -67,6 +65,7 @@ export interface MlCoreSetup { injectUiAppVars: (id: string, callback: () => {}) => any; http: MlHttpServiceSetup; savedObjects: SavedObjectsLegacyService; + coreSavedObjects: SavedObjectsServiceStart; elasticsearch: ElasticsearchServiceSetup; } export interface MlInitializerContext extends PluginInitializerContext { @@ -93,15 +92,11 @@ export interface RouteInitialization { route(route: ServerRoute | ServerRoute[]): void; router: IRouter; xpackMainPlugin: MlXpackMainPlugin; - savedObjects?: SavedObjectsLegacyService; + savedObjects?: SavedObjectsServiceStart; spacesPlugin: any; securityPlugin: any; cloud?: CloudSetup; } -export interface UsageInitialization { - elasticsearchPlugin: ElasticsearchPlugin; - savedObjects: SavedObjectsLegacyService; -} declare module 'kibana/server' { interface RequestHandlerContext { @@ -123,7 +118,7 @@ export class Plugin { public setup(core: MlCoreSetup, plugins: PluginsSetup) { const xpackMainPlugin: MlXpackMainPlugin = plugins.xpackMain; - const { http } = core; + const { http, coreSavedObjects } = core; const pluginId = this.pluginId; mirrorPluginStatus(xpackMainPlugin, plugins.ml); @@ -208,14 +203,10 @@ export class Plugin { const extendedRouteInitializationDeps: RouteInitialization = { ...routeInitializationDeps, config: this.config, - savedObjects: core.savedObjects, + savedObjects: coreSavedObjects, spacesPlugin: plugins.spaces, cloud: plugins.cloud, }; - const usageInitializationDeps: UsageInitialization = { - elasticsearchPlugin: plugins.elasticsearch, - savedObjects: core.savedObjects, - }; const logInitializationDeps: LogInitialization = { log: this.log, @@ -240,7 +231,7 @@ export class Plugin { fileDataVisualizerRoutes(extendedRouteInitializationDeps); initMlServerLog(logInitializationDeps); - makeMlUsageCollector(plugins.usageCollection, usageInitializationDeps); + makeMlUsageCollector(plugins.usageCollection, coreSavedObjects); } public stop() {} diff --git a/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts b/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts index 95f2a9fe7298f0..d5a992c9332930 100644 --- a/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts +++ b/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts @@ -138,7 +138,7 @@ export function fileDataVisualizerRoutes({ // follow-up import calls to just add additional data will include the `id` of the created // index, we'll ignore those and don't increment the counter. if (id === undefined) { - await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects!); + await incrementFileDataVisualizerIndexCreationCount(savedObjects!); } const result = await importData( diff --git a/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts b/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts index 72983129900052..76986b935b993a 100644 --- a/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts +++ b/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts @@ -50,7 +50,7 @@ export function jobAuditMessagesRoutes({ xpackMainPlugin, router }: RouteInitial /** * @apiGroup JobAuditMessages * - * @api {get} /api/ml/results/anomalies_table_data Get all audit messages + * @api {get} /api/ml/job_audit_messages/messages Get all audit messages * @apiName GetAllJobAuditMessages * @apiDescription Returns all audit messages */ From c6f5fdd061d93ad0d67335a658449be92e24640c Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Mon, 24 Feb 2020 17:42:34 +0100 Subject: [PATCH 04/21] Advanced settings UI change to centralize save state (#53693) --- .../advanced_settings/public/_index.scss | 2 +- .../public/management_app/_index.scss | 3 + .../management_app/advanced_settings.scss | 40 +- .../management_app/advanced_settings.tsx | 16 +- .../management_app/components/_index.scss | 1 + .../field/__snapshots__/field.test.tsx.snap | 7069 ++++++++--------- .../components/field/field.test.tsx | 267 +- .../management_app/components/field/field.tsx | 545 +- .../management_app/components/field/index.ts | 2 +- .../form/__snapshots__/form.test.tsx.snap | 1228 ++- .../management_app/components/form/_form.scss | 13 + .../components/form/_index.scss | 1 + .../components/form/form.test.tsx | 114 +- .../management_app/components/form/form.tsx | 280 +- .../public/management_app/types.ts | 13 + .../telemetry_management_section.tsx | 54 +- test/functional/page_objects/settings_page.ts | 6 +- .../translations/translations/ja-JP.json | 11 - .../translations/translations/zh-CN.json | 10 - 19 files changed, 5086 insertions(+), 4589 deletions(-) create mode 100644 src/plugins/advanced_settings/public/management_app/_index.scss create mode 100644 src/plugins/advanced_settings/public/management_app/components/_index.scss create mode 100644 src/plugins/advanced_settings/public/management_app/components/form/_form.scss create mode 100644 src/plugins/advanced_settings/public/management_app/components/form/_index.scss diff --git a/src/plugins/advanced_settings/public/_index.scss b/src/plugins/advanced_settings/public/_index.scss index f3fe78bf6a9c01..d13c37bff32d00 100644 --- a/src/plugins/advanced_settings/public/_index.scss +++ b/src/plugins/advanced_settings/public/_index.scss @@ -17,4 +17,4 @@ * under the License. */ - @import './management_app/advanced_settings'; +@import './management_app/index'; diff --git a/src/plugins/advanced_settings/public/management_app/_index.scss b/src/plugins/advanced_settings/public/management_app/_index.scss new file mode 100644 index 00000000000000..aa1980692f7b7a --- /dev/null +++ b/src/plugins/advanced_settings/public/management_app/_index.scss @@ -0,0 +1,3 @@ +@import './advanced_settings'; + +@import './components/index'; diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.scss b/src/plugins/advanced_settings/public/management_app/advanced_settings.scss index 79b6feccb6b7d6..016edb2817da82 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.scss +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.scss @@ -17,21 +17,27 @@ * under the License. */ -.mgtAdvancedSettings__field { + .mgtAdvancedSettings__field { + * { margin-top: $euiSize; } - &Wrapper { - width: 640px; - @include internetExplorerOnly() { - min-height: 1px; - } + padding-left: $euiSizeS; + margin-left: -$euiSizeS; + &--unsaved { + // Simulates a left side border without shifting content + box-shadow: -$euiSizeXS 0px $euiColorSecondary; } - - &Actions { - padding-top: $euiSizeM; + &--invalid { + // Simulates a left side border without shifting content + box-shadow: -$euiSizeXS 0px $euiColorDanger; + } + @include internetExplorerOnly() { + min-height: 1px; + } + &Row { + padding-left: $euiSizeS; } @include internetExplorerOnly { @@ -40,3 +46,19 @@ } } } + +.mgtAdvancedSettingsForm__unsavedCount { + @include euiBreakpoint('xs', 's') { + display: none; + } +} + +.mgtAdvancedSettingsForm__unsavedCountMessage{ + // Simulates a left side border without shifting content + box-shadow: -$euiSizeXS 0px $euiColorSecondary; + padding-left: $euiSizeS; +} + +.mgtAdvancedSettingsForm__button { + width: 100%; +} diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx index 5057d072e3e415..39312c9340ff9e 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx @@ -38,7 +38,7 @@ import { ComponentRegistry } from '../'; import { getAriaName, toEditableConfig, DEFAULT_CATEGORY } from './lib'; -import { FieldSetting, IQuery } from './types'; +import { FieldSetting, IQuery, SettingsChanges } from './types'; interface AdvancedSettingsProps { enableSaving: boolean; @@ -177,6 +177,13 @@ export class AdvancedSettingsComponent extends Component< }); }; + saveConfig = async (changes: SettingsChanges) => { + const arr = Object.entries(changes).map(([key, value]) => + this.props.uiSettings.set(key, value) + ); + return Promise.all(arr); + }; + render() { const { filteredSettings, query, footerQueryMatched } = this.state; const componentRegistry = this.props.componentRegistry; @@ -205,18 +212,19 @@ export class AdvancedSettingsComponent extends Component<
+
+ + } + fullWidth={true} + title={ +

+ Array test setting + +

+ } > - - -
- - } - title={ -

- Array test setting - -

- } - > - - - - - - - + + + `; exports[`Field for array setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - - - - - default_value - , - } - } - /> - - - - - } - title={ -

- Array test setting - -

- } - > - + default_value + , + } + } /> - - } - isInvalid={false} - label="array:test:setting" - labelType="label" + + + + + } + fullWidth={true} + title={ +

+ Array test setting + +

+ } +> + - - - - - - + + } + label="array:test:setting" + labelType="label" + > + +
+ `; exports[`Field for array setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Array test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Array test setting - - } - type="asterisk" - /> -

- } - > - - - - - - - + + + `; exports[`Field for array setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Array test setting + +

+ } > - - -
- - } - title={ -

- Array test setting - -

- } - > - + + +`; + +exports[`Field for array setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ Array test setting + + } + type="asterisk" + /> +

+ } +> + + + +

- - - - - - + Setting is currently not saved. +

+
+
+ `; exports[`Field for array setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + default_value + , + } + } /> - - - - default_value - , - } - } - /> - - - - } - title={ -

- Array test setting - -

- } - > - - - - - -     - - - } - isInvalid={false} - label="array:test:setting" - labelType="label" - > - - - - - - + + + + } + fullWidth={true} + title={ +

+ Array test setting + +

+ } +> + + + + + +     + + + } + label="array:test:setting" + labelType="label" + > + + + `; exports[`Field for boolean setting should render as read only if saving is disabled 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Boolean test setting + +

+ } > - - -
- - } - title={ -

- Boolean test setting - -

- } - > - - - } - onChange={[Function]} - onKeyDown={[Function]} + - - - - - + } + onChange={[Function]} + /> + + `; exports[`Field for boolean setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - - - - - true - , - } - } - /> - - - - - } - title={ -

- Boolean test setting - -

- } - > - + true + , + } + } /> - - } - isInvalid={false} - label="boolean:test:setting" - labelType="label" + + + + + } + fullWidth={true} + title={ +

+ Boolean test setting + +

+ } +> + - - } - onChange={[Function]} - onKeyDown={[Function]} + + + } + label="boolean:test:setting" + labelType="label" + > + - - - - - + } + onChange={[Function]} + /> +
+ `; exports[`Field for boolean setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Boolean test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Boolean test setting - - } - type="asterisk" - /> -

- } - > - - - } - onChange={[Function]} - onKeyDown={[Function]} + - - - - - + } + onChange={[Function]} + /> + + `; exports[`Field for boolean setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Boolean test setting + +

+ } > - - -
- - } - title={ -

- Boolean test setting - -

+ } - > - - + onChange={[Function]} + /> + + +`; + +exports[`Field for boolean setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ Boolean test setting + + } + type="asterisk" + /> +

+ } +> + + - - - - - + } + onChange={[Function]} + /> + +

+ Setting is currently not saved. +

+
+ + `; exports[`Field for boolean setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + true + , + } + } /> - - - - true - , - } - } - /> - - - - } - title={ -

- Boolean test setting - -

- } - > - - - - - -     - - - } - isInvalid={false} - label="boolean:test:setting" - labelType="label" - > - + + + } + fullWidth={true} + title={ +

+ Boolean test setting + +

+ } +> + + + - } - onChange={[Function]} - onKeyDown={[Function]} + +     + + + } + label="boolean:test:setting" + labelType="label" + > + - - - - - + } + onChange={[Function]} + /> +
+ `; exports[`Field for image setting should render as read only if saving is disabled 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Image test setting + +

+ } > - - -
- - } - title={ -

- Image test setting - -

- } - > - - - - - - - + + + `; exports[`Field for image setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - - - - - null - , - } - } - /> - - - - - } - title={ -

- Image test setting - -

- } - > - + null + , + } + } /> - - } - isInvalid={false} - label="image:test:setting" - labelType="label" + + + + + } + fullWidth={true} + title={ +

+ Image test setting + +

+ } +> + - - - - - - + + } + label="image:test:setting" + labelType="label" + > + +
+ `; exports[`Field for image setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Image test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Image test setting - - } - type="asterisk" - /> -

- } - > - - - - - - - + + + `; exports[`Field for image setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Image test setting + +

+ } > - - -
- - } - title={ -

- Image test setting - -

- } - > - + + +`; + +exports[`Field for image setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ Image test setting + + } + type="asterisk" + /> +

+ } +> + + + +

- - - - - - + Setting is currently not saved. +

+
+
+ `; exports[`Field for image setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + null + , + } + } /> - - - - null - , - } - } - /> - - - - } - title={ -

- Image test setting - -

- } - > - - - - - -     - - - - - - - - } - isInvalid={false} - label="image:test:setting" - labelType="label" - > - - - - - - + + + + } + fullWidth={true} + title={ +

+ Image test setting + +

+ } +> + + + + + +     + + + + + + + + } + label="image:test:setting" + labelType="label" + > + + + `; exports[`Field for json setting should render as read only if saving is disabled 1`] = ` - - - -
+ description={ + +
+ + + - + {} + , + } + } /> - - - - {} - , - } - } - /> - - - - } - title={ -

- Json test setting - -

- } + + + + } + fullWidth={true} + title={ +

+ Json test setting + +

+ } +> + +
- -
- -
-
- - - - + +
+
+ `; exports[`Field for json setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - + {} + , + } + } /> - - - - {} - , - } - } - /> - - - - } - title={ -

- Json test setting - -

- } + + + + } + fullWidth={true} + title={ +

+ Json test setting + +

+ } +> + + + + } + label="json:test:setting" + labelType="label" + > +
- - - + -
- -
-
- - - - + fullWidth={true} + height="auto" + isReadOnly={true} + maxLines={30} + minLines={6} + mode="json" + onChange={[Function]} + setOptions={ + Object { + "showLineNumbers": false, + "tabSize": 2, + } + } + showGutter={false} + theme="textmate" + value="{\\"hello\\": \\"world\\"}" + width="100%" + /> +
+
+ `; exports[`Field for json setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Json test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Json test setting - - } - type="asterisk" - /> -

- } +
- -
- -
-
- - - - + +
+ + `; exports[`Field for json setting should render default value if there is no user value set 1`] = ` - - - -
+ description={ + +
+ + + - + {} + , + } + } /> - - - - {} - , - } - } - /> - - - - } - title={ -

- Json test setting - -

- } + + + + } + fullWidth={true} + title={ +

+ Json test setting + +

+ } +> + + + + + +     + + + } + label="json:test:setting" + labelType="label" + > +
- - - - - -     - - - } - isInvalid={false} - label="json:test:setting" - labelType="label" + +
+
+ +`; + +exports[`Field for json setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ Json test setting + + } + type="asterisk" + /> +

+ } +> + +
+ +
+ +

-

- -
-
- - - - + Setting is currently not saved. +

+ + + `; exports[`Field for json setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + {} + , + } + } /> - - - - {} - , - } - } - /> - - - - } - title={ -

- Json test setting - -

- } + + + + } + fullWidth={true} + title={ +

+ Json test setting + +

+ } +> + + + + + +     + + + } + label="json:test:setting" + labelType="label" + > +
- - - - - -     - - - } - isInvalid={false} - label="json:test:setting" - labelType="label" - > -
- -
-
- - - - + +
+
+ `; exports[`Field for markdown setting should render as read only if saving is disabled 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Markdown test setting + +

+ } > - - -
- - } - title={ -

- Markdown test setting - -

- } +
- -
- -
-
- - - - + +
+ + `; exports[`Field for markdown setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - + null + , + } + } /> - - - - null - , - } - } - /> - - - - } - title={ -

- Markdown test setting - -

- } + + + + } + fullWidth={true} + title={ +

+ Markdown test setting + +

+ } +> + + + + } + label="markdown:test:setting" + labelType="label" + > +
- - - + -
- -
-
- - - - + fullWidth={true} + height="auto" + isReadOnly={true} + maxLines={30} + minLines={6} + mode="markdown" + onChange={[Function]} + setOptions={ + Object { + "showLineNumbers": false, + "tabSize": 2, + } + } + showGutter={false} + theme="textmate" + value="**bold**" + width="100%" + /> +
+
+ `; exports[`Field for markdown setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Markdown test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Markdown test setting - - } - type="asterisk" - /> -

- } +
- -
- -
-
- - - - + +
+ + `; exports[`Field for markdown setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Markdown test setting + +

+ } > - - -
- - } - title={ -

- Markdown test setting - -

- } +
+ +
+ + +`; + +exports[`Field for markdown setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ Markdown test setting + + } + type="asterisk" + /> +

+ } +> + +
- +
+ +

-

- -
-
- - - - + Setting is currently not saved. +

+ + + `; exports[`Field for markdown setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + null + , + } + } /> - - - - null - , - } - } - /> - - - - } - title={ -

- Markdown test setting - -

- } + + + + } + fullWidth={true} + title={ +

+ Markdown test setting + +

+ } +> + + + + + +     + + + } + label="markdown:test:setting" + labelType="label" + > +
- - - - - -     - - - } - isInvalid={false} - label="markdown:test:setting" - labelType="label" - > -
- -
-
- - - - + +
+
+ `; exports[`Field for number setting should render as read only if saving is disabled 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Number test setting + +

+ } > - - -
- - } - title={ -

- Number test setting - -

- } - > - - - - - - - + + + `; exports[`Field for number setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - - - - - 5 - , - } - } - /> - - - - - } - title={ -

- Number test setting - -

- } - > - + 5 + , + } + } /> - - } - isInvalid={false} - label="number:test:setting" - labelType="label" + + + + + } + fullWidth={true} + title={ +

+ Number test setting + +

+ } +> + - - - - - - + + } + label="number:test:setting" + labelType="label" + > + +
+ `; exports[`Field for number setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Number test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Number test setting - - } - type="asterisk" - /> -

- } - > - - - - - - - + + + +`; + +exports[`Field for number setting should render default value if there is no user value set 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ Number test setting + +

+ } +> + + + + `; -exports[`Field for number setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Number test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Number test setting - -

- } - > - + +

- - - - - - + Setting is currently not saved. +

+
+
+ `; exports[`Field for number setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + 5 + , + } + } /> - - - - 5 - , - } - } - /> - - - - } - title={ -

- Number test setting - -

- } - > - - - - - -     - - - } - isInvalid={false} - label="number:test:setting" - labelType="label" - > - - - - - - + + + + } + fullWidth={true} + title={ +

+ Number test setting + +

+ } +> + + + + + +     + + + } + label="number:test:setting" + labelType="label" + > + + + `; exports[`Field for select setting should render as read only if saving is disabled 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Select test setting + +

+ } > - - -
- - } - title={ -

- Select test setting - -

- } - > - - - - - - - + + + `; exports[`Field for select setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - - - - - Orange - , - } - } - /> - - - - - } - title={ -

- Select test setting - -

- } - > - + Orange + , + } + } /> - - } - isInvalid={false} - label="select:test:setting" - labelType="label" + + + + + } + fullWidth={true} + title={ +

+ Select test setting + +

+ } +> + - - - - - - + + } + label="select:test:setting" + labelType="label" + > + +
+ `; exports[`Field for select setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Select test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- Select test setting - - } - type="asterisk" - /> -

- } - > - - - - - - - + + + `; exports[`Field for select setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ Select test setting + +

+ } > - - -
- - } - title={ -

- Select test setting - -

- } - > - + + +`; + +exports[`Field for select setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ Select test setting + + } + type="asterisk" + /> +

+ } +> + + + +

- - - - - - + Setting is currently not saved. +

+
+
+ `; exports[`Field for select setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + Orange + , + } + } /> - - - - Orange - , - } - } - /> - - - - } - title={ -

- Select test setting - -

- } - > - - - - - -     - - - } - isInvalid={false} - label="select:test:setting" - labelType="label" - > - - - - - - + + + + } + fullWidth={true} + title={ +

+ Select test setting + +

+ } +> + + + + + +     + + + } + label="select:test:setting" + labelType="label" + > + + + `; exports[`Field for string setting should render as read only if saving is disabled 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ String test setting + +

+ } > - - -
- - } - title={ -

- String test setting - -

- } - > - - - - - - - + + + `; exports[`Field for string setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - - - - - null - , - } - } - /> - - - - - } - title={ -

- String test setting - -

- } - > - + null + , + } + } /> - - } - isInvalid={false} - label="string:test:setting" - labelType="label" + + + + + } + fullWidth={true} + title={ +

+ String test setting + +

+ } +> + - - - - - - + + } + label="string:test:setting" + labelType="label" + > + +
+ `; exports[`Field for string setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ String test setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- String test setting - - } - type="asterisk" - /> -

- } - > - - - - - - - + + + `; exports[`Field for string setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ String test setting + +

+ } > - - -
- - } - title={ -

- String test setting - -

- } - > - + + +`; + +exports[`Field for string setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ String test setting + + } + type="asterisk" + /> +

+ } +> + + + +

- - - - - - + Setting is currently not saved. +

+
+
+ `; exports[`Field for string setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + null + , + } + } /> - - - - null - , - } - } - /> - - - - } - title={ -

- String test setting - -

- } - > - - - - - -     - - - } - isInvalid={false} - label="string:test:setting" - labelType="label" - > - - - - - - + + + + } + fullWidth={true} + title={ +

+ String test setting + +

+ } +> + + + + + +     + + + } + label="string:test:setting" + labelType="label" + > + + + `; exports[`Field for stringWithValidation setting should render as read only if saving is disabled 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ String test validation setting + +

+ } > - - -
- - } - title={ -

- String test validation setting - -

- } - > - - - - - - - + + + `; exports[`Field for stringWithValidation setting should render as read only with help text if overridden 1`] = ` - - - -
+ description={ + +
+ + + - - - - - foo-default - , - } - } - /> - - - - - } - title={ -

- String test validation setting - -

- } - > - + foo-default + , + } + } /> - - } - isInvalid={false} - label="string:test-validation:setting" - labelType="label" + + + + + } + fullWidth={true} + title={ +

+ String test validation setting + +

+ } +> + - - - - - - + + } + label="string:test-validation:setting" + labelType="label" + > + +
+ `; exports[`Field for stringWithValidation setting should render custom setting icon if it is custom 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ String test validation setting + + } + type="asterisk" + /> +

+ } > - - -
- - } - title={ -

- String test validation setting - - } - type="asterisk" - /> -

- } - > - - - - - - - + + + `; exports[`Field for stringWithValidation setting should render default value if there is no user value set 1`] = ` - +
+ + } + fullWidth={true} + title={ +

+ String test validation setting + +

+ } > - - -
- - } - title={ -

- String test validation setting - -

- } - > - + + +`; + +exports[`Field for stringWithValidation setting should render unsaved value if there are unsaved changes 1`] = ` + +
+ + } + fullWidth={true} + title={ +

+ String test validation setting + + } + type="asterisk" + /> +

+ } +> + + + +

- - - - - - + Setting is currently not saved. +

+
+
+ `; exports[`Field for stringWithValidation setting should render user value if there is user value is set 1`] = ` - - - -
+ description={ + +
+ + + - + foo-default + , + } + } /> - - - - foo-default - , - } - } - /> - - - - } - title={ -

- String test validation setting - -

- } - > - - - - - -     - - - } - isInvalid={false} - label="string:test-validation:setting" - labelType="label" - > - - - - - - + + + + } + fullWidth={true} + title={ +

+ String test validation setting + +

+ } +> + + + + + +     + + + } + label="string:test-validation:setting" + labelType="label" + > + + + `; diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx index 81df22ccf6e43d..8e41fed6858986 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx @@ -20,21 +20,14 @@ import React from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers'; -import { mount } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; import { FieldSetting } from '../../types'; import { UiSettingsType, StringValidation } from '../../../../../../core/public'; import { notificationServiceMock, docLinksServiceMock } from '../../../../../../core/public/mocks'; // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; -import { Field } from './field'; - -jest.mock('ui/notify', () => ({ - toastNotifications: { - addDanger: () => {}, - add: jest.fn(), - }, -})); +import { Field, getEditableValue } from './field'; jest.mock('brace/theme/textmate', () => 'brace/theme/textmate'); jest.mock('brace/mode/markdown', () => 'brace/mode/markdown'); @@ -45,6 +38,18 @@ const defaults = { category: ['category'], }; +const exampleValues = { + array: ['example_value'], + boolean: false, + image: 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=', + json: { foo: 'bar2' }, + markdown: 'Hello World', + number: 1, + select: 'banana', + string: 'hello world', + stringWithValidation: 'foo', +}; + const settings: Record = { array: { name: 'array:test:setting', @@ -161,7 +166,7 @@ const settings: Record = { description: 'Description for String test validation setting', type: 'string', validation: { - regex: new RegExp('/^foo'), + regex: new RegExp('^foo'), message: 'must start with "foo"', }, value: undefined, @@ -182,11 +187,22 @@ const userValues = { string: 'foo', stringWithValidation: 'fooUserValue', }; + const invalidUserValues = { stringWithValidation: 'invalidUserValue', }; -const save = jest.fn(() => Promise.resolve(true)); -const clear = jest.fn(() => Promise.resolve(true)); + +const handleChange = jest.fn(); +const clearChange = jest.fn(); + +const getFieldSettingValue = (wrapper: ReactWrapper, name: string, type: string) => { + const field = findTestSubject(wrapper, `advancedSetting-editField-${name}`); + if (type === 'boolean') { + return field.props()['aria-checked']; + } else { + return field.props().value; + } +}; describe('Field', () => { Object.keys(settings).forEach(type => { @@ -197,8 +213,7 @@ describe('Field', () => { const component = shallowWithI18nProvider( { value: userValues[type], isOverridden: true, }} - save={save} - clear={clear} + handleChange={handleChange} enableSaving={true} toasts={notificationServiceMock.createStartContract().toasts} dockLinks={docLinksServiceMock.createStartContract().links} @@ -232,14 +246,12 @@ describe('Field', () => { const component = shallowWithI18nProvider( ); - expect(component).toMatchSnapshot(); }); @@ -251,8 +263,7 @@ describe('Field', () => { // @ts-ignore value: userValues[type], }} - save={save} - clear={clear} + handleChange={handleChange} enableSaving={true} toasts={notificationServiceMock.createStartContract().toasts} dockLinks={docLinksServiceMock.createStartContract().links} @@ -269,48 +280,44 @@ describe('Field', () => { ...setting, isCustom: true, }} - save={save} - clear={clear} + handleChange={handleChange} enableSaving={true} toasts={notificationServiceMock.createStartContract().toasts} dockLinks={docLinksServiceMock.createStartContract().links} /> ); - expect(component).toMatchSnapshot(); }); - }); - - if (type === 'select') { - it('should use options for rendering values', () => { - const component = mountWithI18nProvider( + it('should render unsaved value if there are unsaved changes', async () => { + const component = shallowWithI18nProvider( ); - const select = findTestSubject(component, `advancedSetting-editField-${setting.name}`); - // @ts-ignore - const labels = select.find('option').map(option => option.prop('value')); - expect(labels).toEqual(['apple', 'orange', 'banana']); + expect(component).toMatchSnapshot(); }); + }); - it('should use optionLabels for rendering labels', () => { + if (type === 'select') { + it('should use options for rendering values and optionsLabels for rendering labels', () => { const component = mountWithI18nProvider( { ); const select = findTestSubject(component, `advancedSetting-editField-${setting.name}`); // @ts-ignore + const values = select.find('option').map(option => option.prop('value')); + expect(values).toEqual(['apple', 'orange', 'banana']); + // @ts-ignore const labels = select.find('option').map(option => option.text()); expect(labels).toEqual(['Apple', 'Orange', 'banana']); }); @@ -328,8 +338,8 @@ describe('Field', () => { { const userValue = userValues[type]; (component.instance() as Field).getImageAsBase64 = ({}: Blob) => Promise.resolve(''); - it('should be able to change value from no value and cancel', async () => { - await (component.instance() as Field).onImageChange([userValue]); - const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-cancelEditField-${setting.name}`).simulate( - 'click' - ); - expect( - (component.instance() as Field).state.unsavedValue === - (component.instance() as Field).state.savedValue - ).toBe(true); - }); - - it('should be able to change value and save', async () => { - await (component.instance() as Field).onImageChange([userValue]); - const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate( - 'click' - ); - expect(save).toBeCalled(); - component.setState({ savedValue: userValue }); + it('should be able to change value and cancel', async () => { + (component.instance() as Field).onImageChange([userValue]); + expect(handleChange).toBeCalled(); await wrapper.setProps({ + unsavedChanges: { + value: userValue, + changeImage: true, + }, setting: { ...(component.instance() as Field).props.setting, value: userValue, }, }); - await (component.instance() as Field).cancelChangeImage(); + expect(clearChange).toBeCalledWith(setting.name); wrapper.update(); }); - it('should be able to change value from existing value and save', async () => { + it('should be able to change value from existing value', async () => { + await wrapper.setProps({ + unsavedChanges: {}, + }); const updated = wrapper.update(); findTestSubject(updated, `advancedSetting-changeImage-${setting.name}`).simulate('click'); - const newUserValue = `${userValue}=`; await (component.instance() as Field).onImageChange([newUserValue]); - const updated2 = wrapper.update(); - findTestSubject(updated2, `advancedSetting-saveEditField-${setting.name}`).simulate( - 'click' - ); - expect(save).toBeCalled(); - component.setState({ savedValue: newUserValue }); - await wrapper.setProps({ - setting: { - ...(component.instance() as Field).props.setting, - value: newUserValue, - }, - }); - wrapper.update(); + expect(handleChange).toBeCalled(); }); it('should be able to reset to default value', async () => { const updated = wrapper.update(); findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click'); - expect(clear).toBeCalled(); + expect(handleChange).toBeCalledWith(setting.name, { + value: getEditableValue(setting.type, setting.defVal), + changeImage: true, + }); }); }); } else if (type === 'markdown' || type === 'json') { describe(`for changing ${type} setting`, () => { const { wrapper, component } = setup(); const userValue = userValues[type]; - const fieldUserValue = userValue; - - it('should be able to change value and cancel', async () => { - (component.instance() as Field).onCodeEditorChange(fieldUserValue as UiSettingsType); - const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-cancelEditField-${setting.name}`).simulate( - 'click' - ); - expect( - (component.instance() as Field).state.unsavedValue === - (component.instance() as Field).state.savedValue - ).toBe(true); - }); - it('should be able to change value and save', async () => { - (component.instance() as Field).onCodeEditorChange(fieldUserValue as UiSettingsType); - const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate( - 'click' - ); - expect(save).toBeCalled(); - component.setState({ savedValue: fieldUserValue }); + it('should be able to change value', async () => { + (component.instance() as Field).onCodeEditorChange(userValue as UiSettingsType); + expect(handleChange).toBeCalledWith(setting.name, { value: userValue }); await wrapper.setProps({ setting: { ...(component.instance() as Field).props.setting, @@ -445,19 +417,21 @@ describe('Field', () => { wrapper.update(); }); + it('should be able to reset to default value', async () => { + const updated = wrapper.update(); + findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click'); + expect(handleChange).toBeCalledWith(setting.name, { + value: getEditableValue(setting.type, setting.defVal), + }); + }); + if (type === 'json') { it('should be able to clear value and have empty object populate', async () => { - (component.instance() as Field).onCodeEditorChange('' as UiSettingsType); + await (component.instance() as Field).onCodeEditorChange('' as UiSettingsType); wrapper.update(); - expect((component.instance() as Field).state.unsavedValue).toEqual('{}'); + expect(handleChange).toBeCalledWith(setting.name, { value: setting.defVal }); }); } - - it('should be able to reset to default value', async () => { - const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click'); - expect(clear).toBeCalled(); - }); }); } else { describe(`for changing ${type} setting`, () => { @@ -470,76 +444,45 @@ describe('Field', () => { // @ts-ignore const invalidUserValue = invalidUserValues[type]; it('should display an error when validation fails', async () => { - (component.instance() as Field).onFieldChange(invalidUserValue); + await (component.instance() as Field).onFieldChange(invalidUserValue); + const expectedUnsavedChanges = { + value: invalidUserValue, + error: (setting.validation as StringValidation).message, + isInvalid: true, + }; + expect(handleChange).toBeCalledWith(setting.name, expectedUnsavedChanges); + wrapper.setProps({ unsavedChanges: expectedUnsavedChanges }); const updated = wrapper.update(); const errorMessage = updated.find('.euiFormErrorText').text(); - expect(errorMessage).toEqual((setting.validation as StringValidation).message); + expect(errorMessage).toEqual(expectedUnsavedChanges.error); }); } - it('should be able to change value and cancel', async () => { - (component.instance() as Field).onFieldChange(fieldUserValue); + it('should be able to change value', async () => { + await (component.instance() as Field).onFieldChange(fieldUserValue); const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-cancelEditField-${setting.name}`).simulate( - 'click' - ); - expect( - (component.instance() as Field).state.unsavedValue === - (component.instance() as Field).state.savedValue - ).toBe(true); + expect(handleChange).toBeCalledWith(setting.name, { value: fieldUserValue }); + updated.setProps({ unsavedChanges: { value: fieldUserValue } }); + const currentValue = getFieldSettingValue(updated, setting.name, type); + expect(currentValue).toEqual(fieldUserValue); }); - it('should be able to change value and save', async () => { - (component.instance() as Field).onFieldChange(fieldUserValue); - const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate( - 'click' - ); - expect(save).toBeCalled(); - component.setState({ savedValue: fieldUserValue }); + it('should be able to reset to default value', async () => { await wrapper.setProps({ - setting: { - ...(component.instance() as Field).props.setting, - value: userValue, - }, + unsavedChanges: {}, + setting: { ...setting, value: fieldUserValue }, }); - wrapper.update(); - }); - - it('should be able to reset to default value', async () => { const updated = wrapper.update(); findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click'); - expect(clear).toBeCalled(); + const expectedEditableValue = getEditableValue(setting.type, setting.defVal); + expect(handleChange).toBeCalledWith(setting.name, { + value: expectedEditableValue, + }); + updated.setProps({ unsavedChanges: { value: expectedEditableValue } }); + const currentValue = getFieldSettingValue(updated, setting.name, type); + expect(currentValue).toEqual(expectedEditableValue); }); }); } }); - - it('should show a reload toast when saving setting requiring a page reload', async () => { - const setting = { - ...settings.string, - requiresPageReload: true, - }; - const toasts = notificationServiceMock.createStartContract().toasts; - const wrapper = mountWithI18nProvider( - - ); - (wrapper.instance() as Field).onFieldChange({ target: { value: 'a new value' } }); - const updated = wrapper.update(); - findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate('click'); - expect(save).toHaveBeenCalled(); - await save(); - expect(toasts.add).toHaveBeenCalledWith( - expect.objectContaining({ - title: expect.stringContaining('Please reload the page'), - }) - ); - }); }); diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx index 7158e3d5e7b3e6..d9c3752d1c0a50 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx @@ -18,17 +18,16 @@ */ import React, { PureComponent, Fragment } from 'react'; -import ReactDOM from 'react-dom'; +import classNames from 'classnames'; import 'brace/theme/textmate'; import 'brace/mode/markdown'; import { EuiBadge, - EuiButton, - EuiButtonEmpty, EuiCode, EuiCodeBlock, + EuiScreenReaderOnly, // @ts-ignore EuiCodeEditor, EuiDescribedFormGroup, @@ -36,23 +35,20 @@ import { EuiFieldText, // @ts-ignore EuiFilePicker, - EuiFlexGroup, - EuiFlexItem, EuiFormRow, EuiIconTip, EuiImage, EuiLink, EuiSpacer, - EuiToolTip, EuiText, EuiSelect, EuiSwitch, EuiSwitchEvent, - keyCodes, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { FieldSetting } from '../../types'; +import { FieldSetting, FieldState } from '../../types'; import { isDefaultValue } from '../../lib'; import { UiSettingsType, @@ -64,71 +60,37 @@ import { interface FieldProps { setting: FieldSetting; - save: (name: string, value: string) => Promise; - clear: (name: string) => Promise; + handleChange: (name: string, value: FieldState) => void; enableSaving: boolean; dockLinks: DocLinksStart['links']; toasts: ToastsStart; + clearChange?: (name: string) => void; + unsavedChanges?: FieldState; + loading?: boolean; } -interface FieldState { - unsavedValue: any; - savedValue: any; - loading: boolean; - isInvalid: boolean; - error: string | null; - changeImage: boolean; - isJsonArray: boolean; -} - -export class Field extends PureComponent { - private changeImageForm: EuiFilePicker | undefined; - constructor(props: FieldProps) { - super(props); - const { type, value, defVal } = this.props.setting; - const editableValue = this.getEditableValue(type, value, defVal); - - this.state = { - isInvalid: false, - error: null, - loading: false, - changeImage: false, - savedValue: editableValue, - unsavedValue: editableValue, - isJsonArray: type === 'json' ? Array.isArray(JSON.parse(String(defVal) || '{}')) : false, - }; - } - - UNSAFE_componentWillReceiveProps(nextProps: FieldProps) { - const { unsavedValue } = this.state; - const { type, value, defVal } = nextProps.setting; - const editableValue = this.getEditableValue(type, value, defVal); - - this.setState({ - savedValue: editableValue, - unsavedValue: value === null || value === undefined ? editableValue : unsavedValue, - }); +export const getEditableValue = ( + type: UiSettingsType, + value: FieldSetting['value'], + defVal?: FieldSetting['defVal'] +) => { + const val = value === null || value === undefined ? defVal : value; + switch (type) { + case 'array': + return (val as string[]).join(', '); + case 'boolean': + return !!val; + case 'number': + return Number(val); + case 'image': + return val; + default: + return val || ''; } +}; - getEditableValue( - type: UiSettingsType, - value: FieldSetting['value'], - defVal: FieldSetting['defVal'] - ) { - const val = value === null || value === undefined ? defVal : value; - switch (type) { - case 'array': - return (val as string[]).join(', '); - case 'boolean': - return !!val; - case 'number': - return Number(val); - case 'image': - return val; - default: - return val || ''; - } - } +export class Field extends PureComponent { + private changeImageForm: EuiFilePicker | undefined = React.createRef(); getDisplayedDefaultValue( type: UiSettingsType, @@ -150,47 +112,60 @@ export class Field extends PureComponent { } } - setLoading(loading: boolean) { - this.setState({ - loading, - }); - } + handleChange = (unsavedChanges: FieldState) => { + this.props.handleChange(this.props.setting.name, unsavedChanges); + }; - clearError() { - this.setState({ - isInvalid: false, - error: null, - }); + resetField = () => { + const { type, defVal } = this.props.setting; + if (type === 'image') { + this.cancelChangeImage(); + return this.handleChange({ + value: getEditableValue(type, defVal), + changeImage: true, + }); + } + return this.handleChange({ value: getEditableValue(type, defVal) }); + }; + + componentDidUpdate(prevProps: FieldProps) { + if ( + prevProps.setting.type === 'image' && + prevProps.unsavedChanges?.value && + !this.props.unsavedChanges?.value + ) { + this.cancelChangeImage(); + } } onCodeEditorChange = (value: UiSettingsType) => { - const { type } = this.props.setting; - const { isJsonArray } = this.state; + const { defVal, type } = this.props.setting; let newUnsavedValue; - let isInvalid = false; - let error = null; + let errorParams = {}; switch (type) { case 'json': + const isJsonArray = Array.isArray(JSON.parse((defVal as string) || '{}')); newUnsavedValue = value.trim() || (isJsonArray ? '[]' : '{}'); try { JSON.parse(newUnsavedValue); } catch (e) { - isInvalid = true; - error = i18n.translate('advancedSettings.field.codeEditorSyntaxErrorMessage', { - defaultMessage: 'Invalid JSON syntax', - }); + errorParams = { + error: i18n.translate('advancedSettings.field.codeEditorSyntaxErrorMessage', { + defaultMessage: 'Invalid JSON syntax', + }), + isInvalid: true, + }; } break; default: newUnsavedValue = value; } - this.setState({ - error, - isInvalid, - unsavedValue: newUnsavedValue, + this.handleChange({ + value: newUnsavedValue, + ...errorParams, }); }; @@ -201,58 +176,44 @@ export class Field extends PureComponent { onFieldChangeEvent = (e: React.ChangeEvent) => this.onFieldChange(e.target.value); - onFieldChange = (value: any) => { - const { type, validation } = this.props.setting; - const { unsavedValue } = this.state; - + onFieldChange = (targetValue: any) => { + const { type, validation, value, defVal } = this.props.setting; let newUnsavedValue; switch (type) { case 'boolean': - newUnsavedValue = !unsavedValue; + const { unsavedChanges } = this.props; + const currentValue = unsavedChanges + ? unsavedChanges.value + : getEditableValue(type, value, defVal); + newUnsavedValue = !currentValue; break; case 'number': - newUnsavedValue = Number(value); + newUnsavedValue = Number(targetValue); break; default: - newUnsavedValue = value; + newUnsavedValue = targetValue; } - let isInvalid = false; - let error = null; + let errorParams = {}; - if (validation && (validation as StringValidationRegex).regex) { + if ((validation as StringValidationRegex)?.regex) { if (!(validation as StringValidationRegex).regex!.test(newUnsavedValue.toString())) { - error = (validation as StringValidationRegex).message; - isInvalid = true; + errorParams = { + error: (validation as StringValidationRegex).message, + isInvalid: true, + }; } } - this.setState({ - unsavedValue: newUnsavedValue, - isInvalid, - error, + this.handleChange({ + value: newUnsavedValue, + ...errorParams, }); }; - onFieldKeyDown = ({ keyCode }: { keyCode: number }) => { - if (keyCode === keyCodes.ENTER) { - this.saveEdit(); - } - if (keyCode === keyCodes.ESCAPE) { - this.cancelEdit(); - } - }; - - onFieldEscape = ({ keyCode }: { keyCode: number }) => { - if (keyCode === keyCodes.ESCAPE) { - this.cancelEdit(); - } - }; - onImageChange = async (files: any[]) => { if (!files.length) { - this.clearError(); this.setState({ unsavedValue: null, }); @@ -266,19 +227,24 @@ export class Field extends PureComponent { if (file instanceof File) { base64Image = (await this.getImageAsBase64(file)) as string; } - const isInvalid = !!(maxSize && maxSize.length && base64Image.length > maxSize.length); - this.setState({ - isInvalid, - error: isInvalid - ? i18n.translate('advancedSettings.field.imageTooLargeErrorMessage', { - defaultMessage: 'Image is too large, maximum size is {maxSizeDescription}', - values: { - maxSizeDescription: maxSize.description, - }, - }) - : null, + + let errorParams = {}; + const isInvalid = !!(maxSize?.length && base64Image.length > maxSize.length); + if (isInvalid) { + errorParams = { + isInvalid, + error: i18n.translate('advancedSettings.field.imageTooLargeErrorMessage', { + defaultMessage: 'Image is too large, maximum size is {maxSizeDescription}', + values: { + maxSizeDescription: maxSize.description, + }, + }), + }; + } + this.handleChange({ changeImage: true, - unsavedValue: base64Image, + value: base64Image, + ...errorParams, }); } catch (err) { this.props.toasts.addDanger( @@ -305,152 +271,62 @@ export class Field extends PureComponent { } changeImage = () => { - this.setState({ + this.handleChange({ + value: null, changeImage: true, }); }; cancelChangeImage = () => { - const { savedValue } = this.state; - - if (this.changeImageForm) { - this.changeImageForm.fileInput.value = null; - this.changeImageForm.handleChange(); - } - - this.setState({ - changeImage: false, - unsavedValue: savedValue, - }); - }; - - cancelEdit = () => { - const { savedValue } = this.state; - this.clearError(); - this.setState({ - unsavedValue: savedValue, - }); - }; - - showPageReloadToast = () => { - if (this.props.setting.requiresPageReload) { - this.props.toasts.add({ - title: i18n.translate('advancedSettings.field.requiresPageReloadToastDescription', { - defaultMessage: 'Please reload the page for the "{settingName}" setting to take effect.', - values: { - settingName: this.props.setting.displayName || this.props.setting.name, - }, - }), - text: element => { - const content = ( - <> - - - window.location.reload()}> - {i18n.translate('advancedSettings.field.requiresPageReloadToastButtonLabel', { - defaultMessage: 'Reload page', - })} - - - - - ); - ReactDOM.render(content, element); - return () => ReactDOM.unmountComponentAtNode(element); - }, - color: 'success', - }); - } - }; - - saveEdit = async () => { - const { name, defVal, type } = this.props.setting; - const { changeImage, savedValue, unsavedValue, isJsonArray } = this.state; - - if (savedValue === unsavedValue) { - return; - } - - let valueToSave = unsavedValue; - let isSameValue = false; - - switch (type) { - case 'array': - valueToSave = valueToSave.split(',').map((val: string) => val.trim()); - isSameValue = valueToSave.join(',') === (defVal as string[]).join(','); - break; - case 'json': - valueToSave = valueToSave.trim(); - valueToSave = valueToSave || (isJsonArray ? '[]' : '{}'); - default: - isSameValue = valueToSave === defVal; - } - - this.setLoading(true); - try { - if (isSameValue) { - await this.props.clear(name); - } else { - await this.props.save(name, valueToSave); - } - - this.showPageReloadToast(); - - if (changeImage) { - this.cancelChangeImage(); - } - } catch (e) { - this.props.toasts.addDanger( - i18n.translate('advancedSettings.field.saveFieldErrorMessage', { - defaultMessage: 'Unable to save {name}', - values: { name }, - }) - ); + if (this.changeImageForm.current) { + this.changeImageForm.current.fileInput.value = null; + this.changeImageForm.current.handleChange({}); } - this.setLoading(false); - }; - - resetField = async () => { - const { name } = this.props.setting; - this.setLoading(true); - try { - await this.props.clear(name); - this.showPageReloadToast(); - this.cancelChangeImage(); - this.clearError(); - } catch (e) { - this.props.toasts.addDanger( - i18n.translate('advancedSettings.field.resetFieldErrorMessage', { - defaultMessage: 'Unable to reset {name}', - values: { name }, - }) - ); + if (this.props.clearChange) { + this.props.clearChange(this.props.setting.name); } - this.setLoading(false); }; - renderField(setting: FieldSetting) { - const { enableSaving } = this.props; - const { loading, changeImage, unsavedValue } = this.state; - const { name, value, type, options, optionLabels = {}, isOverridden, ariaName } = setting; + renderField(id: string, setting: FieldSetting) { + const { enableSaving, unsavedChanges, loading } = this.props; + const { + name, + value, + type, + options, + optionLabels = {}, + isOverridden, + defVal, + ariaName, + } = setting; + const a11yProps: { [key: string]: string } = unsavedChanges + ? { + 'aria-label': ariaName, + 'aria-describedby': id, + } + : { + 'aria-label': ariaName, + }; + const currentValue = unsavedChanges + ? unsavedChanges.value + : getEditableValue(type, value, defVal); switch (type) { case 'boolean': return ( ) : ( ) } - checked={!!unsavedValue} + checked={!!currentValue} onChange={this.onFieldChangeSwitch} disabled={loading || isOverridden || !enableSaving} - onKeyDown={this.onFieldKeyDown} data-test-subj={`advancedSetting-editField-${name}`} - aria-label={ariaName} + {...a11yProps} /> ); case 'markdown': @@ -458,10 +334,10 @@ export class Field extends PureComponent { return (
{ $blockScrolling: Infinity, }} showGutter={false} + fullWidth />
); case 'image': + const changeImage = unsavedChanges?.changeImage; if (!isDefaultValue(setting) && !changeImage) { - return ( - - ); + return ; } else { return ( { - this.changeImageForm = input; - }} - onKeyDown={this.onFieldEscape} + ref={this.changeImageForm} + fullWidth data-test-subj={`advancedSetting-editField-${name}`} /> ); @@ -501,8 +375,8 @@ export class Field extends PureComponent { case 'select': return ( { return { text: optionLabels.hasOwnProperty(option) ? optionLabels[option] : option, @@ -512,31 +386,31 @@ export class Field extends PureComponent { onChange={this.onFieldChangeEvent} isLoading={loading} disabled={loading || isOverridden || !enableSaving} - onKeyDown={this.onFieldKeyDown} + fullWidth data-test-subj={`advancedSetting-editField-${name}`} /> ); case 'number': return ( ); default: return ( ); @@ -699,8 +573,12 @@ export class Field extends PureComponent { } renderResetToDefaultLink(setting: FieldSetting) { - const { ariaName, name } = setting; - if (isDefaultValue(setting)) { + const { defVal, ariaName, name } = setting; + if ( + defVal === this.props.unsavedChanges?.value || + isDefaultValue(setting) || + this.props.loading + ) { return; } return ( @@ -726,7 +604,7 @@ export class Field extends PureComponent { } renderChangeImageLink(setting: FieldSetting) { - const { changeImage } = this.state; + const changeImage = this.props.unsavedChanges?.changeImage; const { type, value, ariaName, name } = setting; if (type !== 'image' || !value || changeImage) { return; @@ -752,84 +630,49 @@ export class Field extends PureComponent { ); } - renderActions(setting: FieldSetting) { - const { ariaName, name } = setting; - const { loading, isInvalid, changeImage, savedValue, unsavedValue } = this.state; - const isDisabled = loading || setting.isOverridden; - - if (savedValue === unsavedValue && !changeImage) { - return; - } - - return ( - - - - - - - - - (changeImage ? this.cancelChangeImage() : this.cancelEdit())} - disabled={isDisabled} - data-test-subj={`advancedSetting-cancelEditField-${name}`} - > - - - - - - ); - } - render() { - const { setting } = this.props; - const { error, isInvalid } = this.state; + const { setting, unsavedChanges } = this.props; + const error = unsavedChanges?.error; + const isInvalid = unsavedChanges?.isInvalid; + + const className = classNames('mgtAdvancedSettings__field', { + 'mgtAdvancedSettings__field--unsaved': unsavedChanges, + 'mgtAdvancedSettings__field--invalid': isInvalid, + }); + const id = setting.name; return ( - - - - - {this.renderField(setting)} - - - - {this.renderActions(setting)} - + + + <> + {this.renderField(id, setting)} + {unsavedChanges && ( + +

+ {unsavedChanges.error + ? unsavedChanges.error + : i18n.translate('advancedSettings.field.settingIsUnsaved', { + defaultMessage: 'Setting is currently not saved.', + })} +

+
+ )} + +
+
); } } diff --git a/src/plugins/advanced_settings/public/management_app/components/field/index.ts b/src/plugins/advanced_settings/public/management_app/components/field/index.ts index 5c86519116fe9f..d1b9b34515532e 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/index.ts +++ b/src/plugins/advanced_settings/public/management_app/components/field/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { Field } from './field'; +export { Field, getEditableValue } from './field'; diff --git a/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap b/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap index 8c471f5f5be9cf..bce9cb67537dbc 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap +++ b/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap @@ -1,449 +1,849 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Form should not render no settings message when instructed not to 1`] = ``; +exports[`Form should not render no settings message when instructed not to 1`] = ` + +
+ + + + + +

+ General +

+
+
+
+ + + +
+
+ + + + + + +

+ Dashboard +

+
+
+
+ + +
+
+ + + + + + +

+ X-pack +

+
+ + + + + + + , + "settingsCount": 9, + } + } + /> + + +
+
+ + +
+
+ +
+
+`; exports[`Form should render no settings message when there are no settings 1`] = ` - - + + + + - - , - } - } + +

+ General +

+
+
+
+ + + +
+
+ -
+ + + + + +

+ Dashboard +

+
+
+
+ + +
+
+ + + + + + +

+ X-pack +

+
+ + + + + + + , + "settingsCount": 9, + } + } + /> + + +
+
+ + +
+
+ +
`; exports[`Form should render normally 1`] = ` - - - - - + + + + -

- General -

-
-
-
- - +

+ General +

+ + + + + - + -
-
- - - - - - + + + + + + + -

- Dashboard -

- -
-
- - +

+ Dashboard +

+ + + + + -
-
- - - - - - -

- X-pack -

-
- +
+
+ + + + + - - - - - - , - "settingsCount": 9, + +

+ X-pack +

+
+ + + + + + + , + "settingsCount": 9, + } } - } - /> - - -
-
- - + + + + + + -
-
- + toasts={Object {}} + /> + + + +
`; exports[`Form should render read-only when saving is disabled 1`] = ` - - - - - + + + + -

- General -

-
-
-
- - +

+ General +

+
+
+ + + - + - - - - - - - - + + + + + + + -

- Dashboard -

- -
-
- - +

+ Dashboard +

+ + + + + -
-
- - - - - - -

- X-pack -

-
- +
+
+ + + + + - - - - - - , - "settingsCount": 9, + +

+ X-pack +

+
+ + + + + + + , + "settingsCount": 9, + } } - } - /> - - -
-
- - + + + + + + -
-
- + toasts={Object {}} + /> + + + +
`; diff --git a/src/plugins/advanced_settings/public/management_app/components/form/_form.scss b/src/plugins/advanced_settings/public/management_app/components/form/_form.scss new file mode 100644 index 00000000000000..02ebb90221d90f --- /dev/null +++ b/src/plugins/advanced_settings/public/management_app/components/form/_form.scss @@ -0,0 +1,13 @@ +@import '@elastic/eui/src/components/header/variables'; +@import '@elastic/eui/src/components/nav_drawer/variables'; + +.mgtAdvancedSettingsForm__bottomBar { + margin-left: $euiNavDrawerWidthCollapsed; + z-index: 9; // Puts it inuder the nav drawer when expanded + &--pushForNav { + margin-left: $euiNavDrawerWidthExpanded; + } + @include euiBreakpoint('xs', 's') { + margin-left: 0; + } +} diff --git a/src/plugins/advanced_settings/public/management_app/components/form/_index.scss b/src/plugins/advanced_settings/public/management_app/components/form/_index.scss new file mode 100644 index 00000000000000..2ef4ef1d20ce99 --- /dev/null +++ b/src/plugins/advanced_settings/public/management_app/components/form/_index.scss @@ -0,0 +1 @@ +@import './form'; diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx index 468cfbfc708205..0e942665b23a9e 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx @@ -18,9 +18,14 @@ */ import React from 'react'; -import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; +import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers'; import { UiSettingsType } from '../../../../../../core/public'; +// @ts-ignore +import { findTestSubject } from '@elastic/eui/lib/test'; + +import { notificationServiceMock } from '../../../../../../core/public/mocks'; +import { SettingsChanges } from '../../types'; import { Form } from './form'; jest.mock('../field', () => ({ @@ -29,6 +34,25 @@ jest.mock('../field', () => ({ }, })); +beforeAll(() => { + const localStorage: Record = { + 'core.chrome.isLocked': true, + }; + + Object.defineProperty(window, 'localStorage', { + value: { + getItem: (key: string) => { + return localStorage[key] || null; + }, + }, + writable: true, + }); +}); + +afterAll(() => { + delete (window as any).localStorage; +}); + const defaults = { requiresPageReload: false, readOnly: false, @@ -43,50 +67,52 @@ const defaults = { const settings = { dashboard: [ { + ...defaults, name: 'dashboard:test:setting', ariaName: 'dashboard test setting', displayName: 'Dashboard test setting', category: ['dashboard'], - ...defaults, + requiresPageReload: true, }, ], general: [ { + ...defaults, name: 'general:test:date', ariaName: 'general test date', displayName: 'Test date', description: 'bar', category: ['general'], - ...defaults, }, { + ...defaults, name: 'setting:test', ariaName: 'setting test', displayName: 'Test setting', description: 'foo', category: ['general'], - ...defaults, }, ], 'x-pack': [ { + ...defaults, name: 'xpack:test:setting', ariaName: 'xpack test setting', displayName: 'X-Pack test setting', category: ['x-pack'], description: 'bar', - ...defaults, }, ], }; + const categories = ['general', 'dashboard', 'hiddenCategory', 'x-pack']; const categoryCounts = { general: 2, dashboard: 1, 'x-pack': 10, }; -const save = (key: string, value: any) => Promise.resolve(true); -const clear = (key: string) => Promise.resolve(true); +const save = jest.fn((changes: SettingsChanges) => Promise.resolve([true])); + const clearQuery = () => {}; describe('Form', () => { @@ -94,10 +120,10 @@ describe('Form', () => { const component = shallowWithI18nProvider( { const component = shallowWithI18nProvider( { const component = shallowWithI18nProvider( { const component = shallowWithI18nProvider( { expect(component).toMatchSnapshot(); }); + + it('should hide bottom bar when clicking on the cancel changes button', async () => { + const wrapper = mountWithI18nProvider( + + ); + (wrapper.instance() as Form).setState({ + unsavedChanges: { + 'dashboard:test:setting': { + value: 'changedValue', + }, + }, + }); + const updated = wrapper.update(); + expect(updated.exists('[data-test-subj="advancedSetting-bottomBar"]')).toEqual(true); + await findTestSubject(updated, `advancedSetting-cancelButton`).simulate('click'); + updated.update(); + expect(updated.exists('[data-test-subj="advancedSetting-bottomBar"]')).toEqual(false); + }); + + it('should show a reload toast when saving setting requiring a page reload', async () => { + const toasts = notificationServiceMock.createStartContract().toasts; + const wrapper = mountWithI18nProvider( + + ); + (wrapper.instance() as Form).setState({ + unsavedChanges: { + 'dashboard:test:setting': { + value: 'changedValue', + }, + }, + }); + const updated = wrapper.update(); + + findTestSubject(updated, `advancedSetting-saveButton`).simulate('click'); + expect(save).toHaveBeenCalled(); + await save({ 'dashboard:test:setting': 'changedValue' }); + expect(toasts.add).toHaveBeenCalledWith( + expect.objectContaining({ + title: expect.stringContaining( + 'One or more settings require you to reload the page to take effect.' + ), + }) + ); + }); }); diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx index 91d587866836ee..ef433dd990d33a 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx @@ -18,7 +18,7 @@ */ import React, { PureComponent, Fragment } from 'react'; - +import classNames from 'classnames'; import { EuiFlexGroup, EuiFlexItem, @@ -27,30 +27,188 @@ import { EuiPanel, EuiSpacer, EuiText, + EuiTextColor, + EuiBottomBar, + EuiButton, + EuiToolTip, + EuiButtonEmpty, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { isEmpty } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { toMountPoint } from '../../../../../kibana_react/public'; import { DocLinksStart, ToastsStart } from '../../../../../../core/public'; import { getCategoryName } from '../../lib'; -import { Field } from '../field'; -import { FieldSetting } from '../../types'; +import { Field, getEditableValue } from '../field'; +import { FieldSetting, SettingsChanges, FieldState } from '../../types'; type Category = string; +const NAV_IS_LOCKED_KEY = 'core.chrome.isLocked'; interface FormProps { settings: Record; + visibleSettings: Record; categories: Category[]; categoryCounts: Record; clearQuery: () => void; - save: (key: string, value: any) => Promise; - clear: (key: string) => Promise; + save: (changes: SettingsChanges) => Promise; showNoResultsMessage: boolean; enableSaving: boolean; dockLinks: DocLinksStart['links']; toasts: ToastsStart; } +interface FormState { + unsavedChanges: { + [key: string]: FieldState; + }; + loading: boolean; +} + export class Form extends PureComponent { + state: FormState = { + unsavedChanges: {}, + loading: false, + }; + + setLoading(loading: boolean) { + this.setState({ + loading, + }); + } + + getSettingByKey = (key: string): FieldSetting | undefined => { + return Object.values(this.props.settings) + .flat() + .find(el => el.name === key); + }; + + getCountOfUnsavedChanges = (): number => { + return Object.keys(this.state.unsavedChanges).length; + }; + + getCountOfHiddenUnsavedChanges = (): number => { + const shownSettings = Object.values(this.props.visibleSettings) + .flat() + .map(setting => setting.name); + return Object.keys(this.state.unsavedChanges).filter(key => !shownSettings.includes(key)) + .length; + }; + + areChangesInvalid = (): boolean => { + const { unsavedChanges } = this.state; + return Object.values(unsavedChanges).some(({ isInvalid }) => isInvalid); + }; + + handleChange = (key: string, change: FieldState) => { + const setting = this.getSettingByKey(key); + if (!setting) { + return; + } + const { type, defVal, value } = setting; + const savedValue = getEditableValue(type, value, defVal); + if (change.value === savedValue) { + return this.clearChange(key); + } + this.setState({ + unsavedChanges: { + ...this.state.unsavedChanges, + [key]: change, + }, + }); + }; + + clearChange = (key: string) => { + if (!this.state.unsavedChanges[key]) { + return; + } + const unsavedChanges = { ...this.state.unsavedChanges }; + delete unsavedChanges[key]; + + this.setState({ + unsavedChanges, + }); + }; + + clearAllUnsaved = () => { + this.setState({ unsavedChanges: {} }); + }; + + saveAll = async () => { + this.setLoading(true); + const { unsavedChanges } = this.state; + + if (isEmpty(unsavedChanges)) { + return; + } + const configToSave: SettingsChanges = {}; + let requiresReload = false; + + Object.entries(unsavedChanges).forEach(([name, { value }]) => { + const setting = this.getSettingByKey(name); + if (!setting) { + return; + } + const { defVal, type, requiresPageReload } = setting; + let valueToSave = value; + let equalsToDefault = false; + switch (type) { + case 'array': + valueToSave = valueToSave.split(',').map((val: string) => val.trim()); + equalsToDefault = valueToSave.join(',') === (defVal as string[]).join(','); + break; + case 'json': + const isArray = Array.isArray(JSON.parse((defVal as string) || '{}')); + valueToSave = valueToSave.trim(); + valueToSave = valueToSave || (isArray ? '[]' : '{}'); + default: + equalsToDefault = valueToSave === defVal; + } + if (requiresPageReload) { + requiresReload = true; + } + configToSave[name] = equalsToDefault ? null : valueToSave; + }); + + try { + await this.props.save(configToSave); + this.clearAllUnsaved(); + if (requiresReload) { + this.renderPageReloadToast(); + } + } catch (e) { + this.props.toasts.addDanger( + i18n.translate('advancedSettings.form.saveErrorMessage', { + defaultMessage: 'Unable to save', + }) + ); + } + this.setLoading(false); + }; + + renderPageReloadToast = () => { + this.props.toasts.add({ + title: i18n.translate('advancedSettings.form.requiresPageReloadToastDescription', { + defaultMessage: 'One or more settings require you to reload the page to take effect.', + }), + text: toMountPoint( + <> + + + window.location.reload()}> + {i18n.translate('advancedSettings.form.requiresPageReloadToastButtonLabel', { + defaultMessage: 'Reload page', + })} + + + + + ), + color: 'success', + }); + }; + renderClearQueryLink(totalSettings: number, currentSettings: number) { const { clearQuery } = this.props; @@ -102,8 +260,9 @@ export class Form extends PureComponent { { return null; } + renderCountOfUnsaved = () => { + const unsavedCount = this.getCountOfUnsavedChanges(); + const hiddenUnsavedCount = this.getCountOfHiddenUnsavedChanges(); + return ( + + + + ); + }; + + renderBottomBar = () => { + const areChangesInvalid = this.areChangesInvalid(); + const bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', { + 'mgtAdvancedSettingsForm__bottomBar--pushForNav': + localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true', + }); + return ( + + + +

{this.renderCountOfUnsaved()}

+
+ + + + + {i18n.translate('advancedSettings.form.cancelButtonLabel', { + defaultMessage: 'Cancel changes', + })} + + + + + + {i18n.translate('advancedSettings.form.saveButtonLabel', { + defaultMessage: 'Save changes', + })} + + + + + +
+
+ ); + }; + render() { - const { settings, categories, categoryCounts, clearQuery } = this.props; + const { unsavedChanges } = this.state; + const { visibleSettings, categories, categoryCounts, clearQuery } = this.props; const currentCategories: Category[] = []; categories.forEach(category => { - if (settings[category] && settings[category].length) { + if (visibleSettings[category] && visibleSettings[category].length) { currentCategories.push(category); } }); return ( - {currentCategories.length - ? currentCategories.map(category => { - return this.renderCategory(category, settings[category], categoryCounts[category]); - }) - : this.maybeRenderNoSettings(clearQuery)} +
+ {currentCategories.length + ? currentCategories.map(category => { + return this.renderCategory( + category, + visibleSettings[category], + categoryCounts[category] + ); + }) + : this.maybeRenderNoSettings(clearQuery)} +
+ {!isEmpty(unsavedChanges) && this.renderBottomBar()}
); } diff --git a/src/plugins/advanced_settings/public/management_app/types.ts b/src/plugins/advanced_settings/public/management_app/types.ts index 05bb5e754563dc..d44a05ce36f5d2 100644 --- a/src/plugins/advanced_settings/public/management_app/types.ts +++ b/src/plugins/advanced_settings/public/management_app/types.ts @@ -47,6 +47,19 @@ export interface FieldSetting { } // until eui searchbar and query are typed + +export interface SettingsChanges { + [key: string]: any; +} + +export interface FieldState { + value?: any; + changeImage?: boolean; + loading?: boolean; + isInvalid?: boolean; + error?: string | null; +} + export interface IQuery { ast: any; // incomplete text: string; diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index 20c8873b132727..bf14c33a480481 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -33,8 +33,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { PRIVACY_STATEMENT_URL } from '../../common/constants'; import { OptInExampleFlyout } from './opt_in_example_flyout'; -// @ts-ignore import { Field } from '../../../advanced_settings/public'; +import { ToastsStart } from '../../../../core/public/'; import { TelemetryService } from '../services/telemetry_service'; const SEARCH_TERMS = ['telemetry', 'usage', 'data', 'usage data']; @@ -44,12 +44,14 @@ interface Props { showAppliesSettingMessage: boolean; enableSaving: boolean; query?: any; + toasts: ToastsStart; } interface State { processing: boolean; showExample: boolean; queryMatches: boolean | null; + enabled: boolean; } export class TelemetryManagementSection extends Component { @@ -57,6 +59,7 @@ export class TelemetryManagementSection extends Component { processing: false, showExample: false, queryMatches: null, + enabled: this.props.telemetryService.getIsOptedIn() || false, }; UNSAFE_componentWillReceiveProps(nextProps: Props) { @@ -79,7 +82,7 @@ export class TelemetryManagementSection extends Component { render() { const { telemetryService } = this.props; - const { showExample, queryMatches } = this.state; + const { showExample, queryMatches, enabled, processing } = this.state; if (!telemetryService.getCanChangeOptInStatus()) { return null; @@ -119,7 +122,7 @@ export class TelemetryManagementSection extends Component { displayName: i18n.translate('telemetry.provideUsageStatisticsTitle', { defaultMessage: 'Provide usage statistics', }), - value: telemetryService.getIsOptedIn(), + value: enabled, description: this.renderDescription(), defVal: true, ariaName: i18n.translate('telemetry.provideUsageStatisticsAriaName', { @@ -127,10 +130,10 @@ export class TelemetryManagementSection extends Component { }), } as any } + loading={processing} dockLinks={null as any} toasts={null as any} - save={this.toggleOptIn} - clear={this.toggleOptIn} + handleChange={this.toggleOptIn} enableSaving={this.props.enableSaving} /> @@ -151,13 +154,13 @@ export class TelemetryManagementSection extends Component {

), @@ -200,20 +203,35 @@ export class TelemetryManagementSection extends Component { ); toggleOptIn = async (): Promise => { - const { telemetryService } = this.props; - const newOptInValue = !telemetryService.getIsOptedIn(); + const { telemetryService, toasts } = this.props; + const newOptInValue = !this.state.enabled; return new Promise((resolve, reject) => { - this.setState({ processing: true }, async () => { - try { - await telemetryService.setOptIn(newOptInValue); - this.setState({ processing: false }); - resolve(true); - } catch (err) { - this.setState({ processing: false }); - reject(err); + this.setState( + { + processing: true, + enabled: newOptInValue, + }, + async () => { + try { + await telemetryService.setOptIn(newOptInValue); + this.setState({ processing: false }); + toasts.addSuccess( + newOptInValue + ? i18n.translate('telemetry.optInSuccessOn', { + defaultMessage: 'Usage data collection turned on.', + }) + : i18n.translate('telemetry.optInSuccessOff', { + defaultMessage: 'Usage data collection turned off.', + }) + ); + resolve(true); + } catch (err) { + this.setState({ processing: false }); + reject(err); + } } - }); + ); }); }; diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index d7e5064cf72805..ff340c6b0abcde 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -94,7 +94,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider `[data-test-subj="advancedSetting-editField-${propertyName}"] option[value="${propertyValue}"]` ); await PageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.click(`advancedSetting-saveEditField-${propertyName}`); + await testSubjects.click(`advancedSetting-saveButton`); await PageObjects.header.waitUntilLoadingHasFinished(); } @@ -102,14 +102,14 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider const input = await testSubjects.find(`advancedSetting-editField-${propertyName}`); await input.clearValue(); await input.type(propertyValue); - await testSubjects.click(`advancedSetting-saveEditField-${propertyName}`); + await testSubjects.click(`advancedSetting-saveButton`); await PageObjects.header.waitUntilLoadingHasFinished(); } async toggleAdvancedSettingCheckbox(propertyName: string) { testSubjects.click(`advancedSetting-editField-${propertyName}`); await PageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.click(`advancedSetting-saveEditField-${propertyName}`); + await testSubjects.click(`advancedSetting-saveButton`); await PageObjects.header.waitUntilLoadingHasFinished(); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4b06645cdfe043..78bb39dd22dea1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1618,8 +1618,6 @@ "advancedSettings.categoryNames.timelionLabel": "Timelion", "advancedSettings.categoryNames.visualizationsLabel": "ビジュアライゼーション", "advancedSettings.categorySearchLabel": "カテゴリー", - "advancedSettings.field.cancelEditingButtonAriaLabel": "{ariaName} の編集をキャンセル", - "advancedSettings.field.cancelEditingButtonLabel": "キャンセル", "advancedSettings.field.changeImageLinkAriaLabel": "{ariaName} を変更", "advancedSettings.field.changeImageLinkText": "画像を変更", "advancedSettings.field.codeEditorSyntaxErrorMessage": "無効な JSON 構文", @@ -1632,17 +1630,10 @@ "advancedSettings.field.imageTooLargeErrorMessage": "画像が大きすぎます。最大サイズは {maxSizeDescription} です", "advancedSettings.field.offLabel": "オフ", "advancedSettings.field.onLabel": "オン", - "advancedSettings.field.requiresPageReloadToastButtonLabel": "ページを再読み込み", - "advancedSettings.field.requiresPageReloadToastDescription": "「{settingName}」設定を有効にするには、ページを再読み込みしてください。", - "advancedSettings.field.resetFieldErrorMessage": "{name} をリセットできませんでした", "advancedSettings.field.resetToDefaultLinkAriaLabel": "{ariaName} をデフォルトにリセット", "advancedSettings.field.resetToDefaultLinkText": "デフォルトにリセット", - "advancedSettings.field.saveButtonAriaLabel": "{ariaName} を保存", - "advancedSettings.field.saveButtonLabel": "保存", - "advancedSettings.field.saveFieldErrorMessage": "{name} を保存できませんでした", "advancedSettings.form.clearNoSearchResultText": "(検索結果を消去)", "advancedSettings.form.clearSearchResultText": "(検索結果を消去)", - "advancedSettings.form.noSearchResultText": "設定が見つかりませんでした {clearSearch}", "advancedSettings.form.searchResultText": "検索用語により {settingsCount} 件の設定が非表示になっています {clearSearch}", "advancedSettings.pageTitle": "設定", "advancedSettings.searchBar.unableToParseQueryErrorMessage": "クエリをパースできません", @@ -2474,8 +2465,6 @@ "statusPage.statusApp.statusTitle": "プラグインステータス", "statusPage.statusTable.columns.idHeader": "ID", "statusPage.statusTable.columns.statusHeader": "ステータス", - "telemetry.callout.appliesSettingTitle": "この設定は {allOfKibanaText} に適用されます", - "telemetry.callout.appliesSettingTitle.allOfKibanaText": "Kibana のすべて", "telemetry.callout.clusterStatisticsDescription": "これは収集される基本的なクラスター統計の例です。インデックス、シャード、ノードの数が含まれます。監視がオンになっているかどうかなどのハイレベルの使用統計も含まれます。", "telemetry.callout.clusterStatisticsTitle": "クラスター統計", "telemetry.callout.errorLoadingClusterStatisticsDescription": "クラスター統計の取得中に予期せぬエラーが発生しました。Elasticsearch、Kibana、またはネットワークのエラーが原因の可能性があります。Kibana を確認し、ページを再読み込みして再試行してください。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ecf4dfbb33be65..fc9dacf0e50f7b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1618,8 +1618,6 @@ "advancedSettings.categoryNames.timelionLabel": "Timelion", "advancedSettings.categoryNames.visualizationsLabel": "可视化", "advancedSettings.categorySearchLabel": "类别", - "advancedSettings.field.cancelEditingButtonAriaLabel": "取消编辑 {ariaName}", - "advancedSettings.field.cancelEditingButtonLabel": "取消", "advancedSettings.field.changeImageLinkAriaLabel": "更改 {ariaName}", "advancedSettings.field.changeImageLinkText": "更改图片", "advancedSettings.field.codeEditorSyntaxErrorMessage": "JSON 语法无效", @@ -1632,14 +1630,8 @@ "advancedSettings.field.imageTooLargeErrorMessage": "图像过大,最大大小为 {maxSizeDescription}", "advancedSettings.field.offLabel": "关闭", "advancedSettings.field.onLabel": "开启", - "advancedSettings.field.requiresPageReloadToastButtonLabel": "重新加载页面", - "advancedSettings.field.requiresPageReloadToastDescription": "请重新加载页面,以使“{settingName}”设置生效。", - "advancedSettings.field.resetFieldErrorMessage": "无法重置 {name}", "advancedSettings.field.resetToDefaultLinkAriaLabel": "将 {ariaName} 重置为默认值", "advancedSettings.field.resetToDefaultLinkText": "重置为默认值", - "advancedSettings.field.saveButtonAriaLabel": "保存 {ariaName}", - "advancedSettings.field.saveButtonLabel": "保存", - "advancedSettings.field.saveFieldErrorMessage": "无法保存 {name}", "advancedSettings.form.clearNoSearchResultText": "(清除搜索)", "advancedSettings.form.clearSearchResultText": "(清除搜索)", "advancedSettings.form.noSearchResultText": "未找到设置{clearSearch}", @@ -2474,8 +2466,6 @@ "statusPage.statusApp.statusTitle": "插件状态", "statusPage.statusTable.columns.idHeader": "ID", "statusPage.statusTable.columns.statusHeader": "状态", - "telemetry.callout.appliesSettingTitle": "此设置适用于{allOfKibanaText}", - "telemetry.callout.appliesSettingTitle.allOfKibanaText": "所有 Kibana。", "telemetry.callout.clusterStatisticsDescription": "这是我们将收集的基本集群统计信息的示例。其包括索引、分片和节点的数目。还包括概括性的使用情况统计信息,例如监测是否打开。", "telemetry.callout.clusterStatisticsTitle": "集群统计信息", "telemetry.callout.errorLoadingClusterStatisticsDescription": "尝试提取集群统计信息时发生意外错误。发生此问题的原因可能是 Elasticsearch 出故障、Kibana 出故障或者有网络错误。检查 Kibana,然后重新加载页面并重试。", From 256e4ab67c7fccae9aae38aac22f3788466f8b2f Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Mon, 24 Feb 2020 09:40:37 -0800 Subject: [PATCH 05/21] Adding xpack.encryptedSavedObjects.encryptionKey to docker allow-list (#58291) Co-authored-by: Elastic Machine --- .../os_packages/docker_generator/resources/bin/kibana-docker | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index 34ba25f92beb6a..d4d2e86e1e96bf 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -142,6 +142,7 @@ kibana_vars=( xpack.code.security.enableGitCertCheck xpack.code.security.gitHostWhitelist xpack.code.security.gitProtocolWhitelist + xpack.encryptedSavedObjects.encryptionKey xpack.graph.enabled xpack.graph.canEditDrillDownUrls xpack.graph.savePolicy From b88b99140bc0d63036c0789d1ddc8dc9597e2b5e Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 24 Feb 2020 18:44:24 +0100 Subject: [PATCH 06/21] [ML] Fix transforms license check. (#58343) Fixes an error where the transforms page would load blank with an expired license. Fixes the issue by adding a type guard. With an expired license, the page now renders again correctly the error message. --- .../lib/authorization/components/common.ts | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts b/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts index 5aec2ac041db33..27556e0d673a8e 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts +++ b/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts @@ -21,19 +21,33 @@ export interface Privileges { missingPrivileges: MissingPrivileges; } +function isPrivileges(arg: any): arg is Privileges { + return ( + typeof arg === 'object' && + arg !== null && + arg.hasOwnProperty('hasAllPrivileges') && + typeof arg.hasAllPrivileges === 'boolean' && + arg.hasOwnProperty('missingPrivileges') && + typeof arg.missingPrivileges === 'object' && + arg.missingPrivileges !== null + ); +} + export interface MissingPrivileges { [key: string]: string[] | undefined; } export const toArray = (value: string | string[]): string[] => Array.isArray(value) ? value : [value]; -export const hasPrivilegeFactory = (privileges: Privileges) => (privilege: Privilege) => { +export const hasPrivilegeFactory = (privileges: Privileges | undefined | null) => ( + privilege: Privilege +) => { const [section, requiredPrivilege] = privilege; - if (!privileges.missingPrivileges[section]) { + if (isPrivileges(privileges) && !privileges.missingPrivileges[section]) { // if the section does not exist in our missingPrivileges, everything is OK return true; } - if (privileges.missingPrivileges[section]!.length === 0) { + if (isPrivileges(privileges) && privileges.missingPrivileges[section]!.length === 0) { return true; } if (requiredPrivilege === '*') { @@ -42,7 +56,9 @@ export const hasPrivilegeFactory = (privileges: Privileges) => (privilege: Privi } // If we require _some_ privilege, we make sure that the one // we require is *not* in the missingPrivilege array - return !privileges.missingPrivileges[section]!.includes(requiredPrivilege); + return ( + isPrivileges(privileges) && !privileges.missingPrivileges[section]!.includes(requiredPrivilege) + ); }; // create the text for button's tooltips if the user From 12f35d5788f5250803434b6d8f25d9df82ac0940 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 24 Feb 2020 10:23:44 -0800 Subject: [PATCH 07/21] Add ingest manager header component (#58300) Co-authored-by: Elastic Machine --- x-pack/legacy/plugins/ingest_manager/index.ts | 2 + .../ingest_manager/components/header.tsx | 62 ++++ .../ingest_manager/components/index.ts | 1 + .../ingest_manager/layouts/default.tsx | 29 +- .../ingest_manager/layouts/index.tsx | 1 + .../ingest_manager/layouts/with_header.tsx | 29 ++ .../sections/agent_config/list_page/index.tsx | 291 +++++++++--------- ...illustration_kibana_getting_started@2x.png | Bin 0 -> 131132 bytes .../ingest_manager/sections/epm/index.tsx | 69 ++++- .../ingest_manager/sections/fleet/index.tsx | 46 ++- .../sections/overview/index.tsx | 32 +- 11 files changed, 397 insertions(+), 165 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png diff --git a/x-pack/legacy/plugins/ingest_manager/index.ts b/x-pack/legacy/plugins/ingest_manager/index.ts index c20cc7225d780b..7ed5599b234a34 100644 --- a/x-pack/legacy/plugins/ingest_manager/index.ts +++ b/x-pack/legacy/plugins/ingest_manager/index.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { resolve } from 'path'; import { savedObjectMappings, OUTPUT_SAVED_OBJECT_TYPE, @@ -18,6 +19,7 @@ import { export function ingestManager(kibana: any) { return new kibana.Plugin({ id: 'ingestManager', + publicDir: resolve(__dirname, '../../../plugins/ingest_manager/public'), uiExports: { savedObjectSchemas: { [AGENT_CONFIG_SAVED_OBJECT_TYPE]: { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx new file mode 100644 index 00000000000000..0936b5dcfed109 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx @@ -0,0 +1,62 @@ +/* + * 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 React from 'react'; +import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; +import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; + +const Container = styled.div` + border-bottom: ${props => props.theme.eui.euiBorderThin}; + background-color: ${props => props.theme.eui.euiPageBackgroundColor}; +`; + +const Wrapper = styled.div` + max-width: 1200px; + margin-left: auto; + margin-right: auto; + padding-top: ${props => props.theme.eui.paddingSizes.xl}; +`; + +const Tabs = styled(EuiTabs)` + top: 1px; + &:before { + height: 0px; + } +`; + +export interface HeaderProps { + leftColumn?: JSX.Element; + rightColumn?: JSX.Element; + tabs?: EuiTabProps[]; +} + +export const Header: React.FC = ({ leftColumn, rightColumn, tabs }) => ( + + + + {leftColumn ? {leftColumn} : null} + {rightColumn ? {rightColumn} : null} + + + {tabs ? ( + + + {tabs.map(props => ( + + {props.name} + + ))} + + + ) : ( + + + + )} + + + +); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts index 5133d825884944..b6bb29462c5693 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts @@ -4,3 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ export { Loading } from './loading'; +export { Header, HeaderProps } from './header'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx index eaf49fed3d9338..f99d1bfe500268 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx @@ -4,17 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { - EuiPage, - EuiPageBody, - EuiTabs, - EuiTab, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, -} from '@elastic/eui'; +import styled from 'styled-components'; +import { EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import euiStyled from '../../../../../../legacy/common/eui_styled_components'; import { Section } from '../sections'; import { useLink, useConfig } from '../hooks'; import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH } from '../constants'; @@ -24,7 +16,12 @@ interface Props { children?: React.ReactNode; } -const Nav = euiStyled.nav` +const Container = styled.div` + min-height: calc(100vh - ${props => props.theme.eui.euiHeaderChildSize}); + background: ${props => props.theme.eui.euiColorEmptyShade}; +`; + +const Nav = styled.nav` background: ${props => props.theme.eui.euiColorEmptyShade}; border-bottom: ${props => props.theme.eui.euiBorderThin}; padding: ${props => @@ -32,13 +29,13 @@ const Nav = euiStyled.nav` .euiTabs { padding-left: 3px; margin-left: -3px; - }; + } `; export const DefaultLayout: React.FunctionComponent = ({ section, children }) => { const { epm, fleet } = useConfig(); return ( -

+ - - {children} - -
+ {children} + ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx index 858951bd0d38f4..a9ef7f16562600 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx @@ -4,3 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ export { DefaultLayout } from './default'; +export { WithHeaderLayout } from './with_header'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx new file mode 100644 index 00000000000000..d59c99316c8b8e --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx @@ -0,0 +1,29 @@ +/* + * 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 React, { Fragment } from 'react'; +import styled from 'styled-components'; +import { EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui'; +import { Header, HeaderProps } from '../components'; + +const Page = styled(EuiPage)` + background: ${props => props.theme.eui.euiColorEmptyShade}; +`; + +interface Props extends HeaderProps { + children?: React.ReactNode; +} + +export const WithHeaderLayout: React.FC = ({ children, ...rest }) => ( + +
+ + + + {children} + + + +); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx index ca9fb195166f6b..ef5a38d4869015 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx @@ -5,9 +5,6 @@ */ import React, { useState } from 'react'; import { - EuiPageBody, - EuiPageContent, - EuiTitle, EuiSpacer, EuiText, EuiFlexGroup, @@ -24,11 +21,43 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { AgentConfig } from '../../../types'; import { DEFAULT_AGENT_CONFIG_ID, AGENT_CONFIG_DETAILS_PATH } from '../../../constants'; +import { WithHeaderLayout } from '../../../layouts'; // import { SearchBar } from '../../../components'; import { useGetAgentConfigs, usePagination, useLink } from '../../../hooks'; import { AgentConfigDeleteProvider } from '../components'; import { CreateAgentConfigFlyout } from './components'; +const AgentConfigListPageLayout: React.FunctionComponent = ({ children }) => ( + + + +

+ +

+
+
+ + +

+ +

+
+
+ + } + > + {children} +
+); + export const AgentConfigListPage: React.FunctionComponent<{}> = () => { // Create agent config flyout state const [isCreateAgentConfigFlyoutOpen, setIsCreateAgentConfigFlyoutOpen] = useState( @@ -123,71 +152,46 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { ); return ( - - - {isCreateAgentConfigFlyoutOpen ? ( - { - setIsCreateAgentConfigFlyoutOpen(false); - sendRequest(); - }} - /> - ) : null} - - -

- -

-
- - - - - - - - - - - - - - {selectedAgentConfigs.length ? ( - - - {deleteAgentConfigsPrompt => ( - { - deleteAgentConfigsPrompt( - selectedAgentConfigs.map(agentConfig => agentConfig.id), - () => { - sendRequest(); - setSelectedAgentConfigs([]); - } - ); + + {isCreateAgentConfigFlyoutOpen ? ( + { + setIsCreateAgentConfigFlyoutOpen(false); + sendRequest(); + }} + /> + ) : null} + + {selectedAgentConfigs.length ? ( + + + {deleteAgentConfigsPrompt => ( + { + deleteAgentConfigsPrompt( + selectedAgentConfigs.map(agentConfig => agentConfig.id), + () => { + sendRequest(); + setSelectedAgentConfigs([]); + } + ); + }} + > + - - - )} - - - ) : null} - - {/* + + )} + + + ) : null} + + {/* { setPagination({ @@ -198,83 +202,82 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { }} fieldPrefix={AGENT_CONFIG_SAVED_OBJECT_TYPE} /> */} - - - sendRequest()}> - - - - - setIsCreateAgentConfigFlyoutOpen(true)} - > - - - - + + + sendRequest()}> + + + + + setIsCreateAgentConfigFlyoutOpen(true)} + > + + + + - - - ) : !search.trim() && agentConfigData?.total === 0 ? ( - emptyPrompt - ) : ( - setSearch('')}> - - - ), - }} - /> - ) - } - items={agentConfigData ? agentConfigData.items : []} - itemId="id" - columns={columns} - isSelectable={true} - selection={{ - selectable: (agentConfig: AgentConfig) => agentConfig.id !== DEFAULT_AGENT_CONFIG_ID, - onSelectionChange: (newSelectedAgentConfigs: AgentConfig[]) => { - setSelectedAgentConfigs(newSelectedAgentConfigs); - }, - }} - pagination={{ - pageIndex: pagination.currentPage - 1, - pageSize: pagination.pageSize, - totalItemCount: agentConfigData ? agentConfigData.total : 0, - }} - onChange={({ page }: { page: { index: number; size: number } }) => { - const newPagination = { - ...pagination, - currentPage: page.index + 1, - pageSize: page.size, - }; - setPagination(newPagination); - sendRequest(); // todo: fix this to send pagination options - }} - /> -
-
+ + + ) : !search.trim() && agentConfigData?.total === 0 ? ( + emptyPrompt + ) : ( + setSearch('')}> + + + ), + }} + /> + ) + } + items={agentConfigData ? agentConfigData.items : []} + itemId="id" + columns={columns} + isSelectable={true} + selection={{ + selectable: (agentConfig: AgentConfig) => agentConfig.id !== DEFAULT_AGENT_CONFIG_ID, + onSelectionChange: (newSelectedAgentConfigs: AgentConfig[]) => { + setSelectedAgentConfigs(newSelectedAgentConfigs); + }, + }} + pagination={{ + pageIndex: pagination.currentPage - 1, + pageSize: pagination.pageSize, + totalItemCount: agentConfigData ? agentConfigData.total : 0, + }} + onChange={({ page }: { page: { index: number; size: number } }) => { + const newPagination = { + ...pagination, + currentPage: page.index + 1, + pageSize: page.size, + }; + setPagination(newPagination); + sendRequest(); // todo: fix this to send pagination options + }} + /> + ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cad64be0b6e36e79012970814c7018776bee5f73 GIT binary patch literal 131132 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ͤum)*z|9JCu4oQ+`+gia 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 { const { epm } = useConfig(); - return epm.enabled ?
hello world - epm app
: null; + const { http } = useCore(); + + if (!epm.enabled) { + return null; + } + + return ( + + + +

+ +

+
+
+ + +

+ +

+
+
+ + } + rightColumn={ + + + + } + tabs={[ + { + id: 'all_packages', + name: 'All packages', + isSelected: true, + }, + { + id: 'installed_packages', + name: 'Installed packages', + }, + ]} + > + hello world - fleet app +
+ ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx index 978414769004d2..c4e8c576a1d7db 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx @@ -4,9 +4,53 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { WithHeaderLayout } from '../../layouts'; import { useConfig } from '../../hooks'; export const FleetApp: React.FunctionComponent = () => { const { fleet } = useConfig(); - return fleet.enabled ?
hello world - fleet app
: null; + if (!fleet.enabled) { + return null; + } + + return ( + + + +

+ +

+
+
+ + +

+ +

+
+
+ + } + tabs={[ + { + id: 'agents', + name: 'Agents', + isSelected: true, + }, + { + id: 'enrollment_keys', + name: 'Enrollment keys', + }, + ]} + > + hello world - fleet app +
+ ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx index da4a78a39e2fea..ea6b045f504ec1 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx @@ -4,7 +4,37 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; +import { EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { WithHeaderLayout } from '../../layouts'; export const IngestManagerOverview: React.FunctionComponent = () => { - return
Ingest manager overview page
; + return ( + + + +

+ +

+
+
+ + +

+ +

+
+
+ + } + /> + ); }; From 63d5e382d888728f6a96eede327c61eaae271989 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Mon, 24 Feb 2020 13:40:33 -0500 Subject: [PATCH 08/21] removes extraHandlers (#58336) --- .../public/embeddable/visualize_embeddable.ts | 4 +--- src/plugins/expressions/public/loader.ts | 6 +++--- src/plugins/expressions/public/render.ts | 4 ++-- src/plugins/expressions/public/types/index.ts | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts index 72a0ef72b56937..32bbae13b79b85 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -369,9 +369,7 @@ export class VisualizeEmbeddable extends Embeddable { + render = async (data: any, uiState: any = {}) => { if (!data || typeof data !== 'object') { return this.handleRenderError(new Error('invalid data provided to the expression renderer')); } @@ -119,7 +119,7 @@ export class ExpressionRenderHandler { .get(data.as)! .render(this.element, data.value, { ...this.handlers, - ...extraHandlers, + uiState, } as any); } catch (e) { return this.handleRenderError(e); diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index c77698d3661c24..b5781ef213fd0a 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -48,7 +48,7 @@ export interface IExpressionLoaderParams { disableCaching?: boolean; customFunctions?: []; customRenderers?: []; - extraHandlers?: Record; + uiState?: unknown; inspectorAdapters?: Adapters; onRenderError?: RenderErrorHandlerFnType; } From d1df0e5da5a642b889bbe626a5ebaad8bf973f9a Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 24 Feb 2020 20:47:32 +0200 Subject: [PATCH 09/21] [Telemetry] Server backpressure mechanism (#57556) * finish backpressure mechanism * usage fetch method send OPTIONS before sending POST * add unit test for get_telemetry_failure_details * get currentVersion in constructor * fix type check Co-authored-by: Elastic Machine --- .../core_plugins/telemetry/mappings.json | 7 ++ .../core_plugins/telemetry/server/fetcher.ts | 68 ++++++++++--- .../get_telemetry_failure_details.test.ts | 96 +++++++++++++++++++ .../get_telemetry_failure_details.ts | 45 +++++++++ .../server/telemetry_config/index.ts | 4 + .../server/telemetry_repository/index.ts | 2 + src/plugins/telemetry/public/plugin.ts | 12 +-- 7 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts create mode 100644 src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts diff --git a/src/legacy/core_plugins/telemetry/mappings.json b/src/legacy/core_plugins/telemetry/mappings.json index a88372a5578e87..fa9cc93d6363a0 100644 --- a/src/legacy/core_plugins/telemetry/mappings.json +++ b/src/legacy/core_plugins/telemetry/mappings.json @@ -17,6 +17,13 @@ }, "userHasSeenNotice": { "type": "boolean" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "ignore_above": 256, + "type": "keyword" } } } diff --git a/src/legacy/core_plugins/telemetry/server/fetcher.ts b/src/legacy/core_plugins/telemetry/server/fetcher.ts index 6e16328c4abd89..d30ee100668134 100644 --- a/src/legacy/core_plugins/telemetry/server/fetcher.ts +++ b/src/legacy/core_plugins/telemetry/server/fetcher.ts @@ -21,19 +21,26 @@ import moment from 'moment'; // @ts-ignore import fetch from 'node-fetch'; import { telemetryCollectionManager } from './collection_manager'; -import { getTelemetryOptIn, getTelemetrySendUsageFrom } from './telemetry_config'; +import { + getTelemetryOptIn, + getTelemetrySendUsageFrom, + getTelemetryFailureDetails, +} from './telemetry_config'; import { getTelemetrySavedObject, updateTelemetrySavedObject } from './telemetry_repository'; import { REPORT_INTERVAL_MS } from '../common/constants'; export class FetcherTask { - private readonly checkDurationMs = 60 * 1000 * 5; + private readonly initialCheckDelayMs = 60 * 1000 * 5; + private readonly checkIntervalMs = 60 * 1000 * 60 * 12; private intervalId?: NodeJS.Timeout; private lastReported?: number; + private currentVersion: string; private isSending = false; private server: any; constructor(server: any) { this.server = server; + this.currentVersion = this.server.config().get('pkg.version'); } private getInternalRepository = () => { @@ -52,6 +59,9 @@ export class FetcherTask { const allowChangingOptInStatus = config.get('telemetry.allowChangingOptInStatus'); const configTelemetryOptIn = config.get('telemetry.optIn'); const telemetryUrl = config.get('telemetry.url') as string; + const { failureCount, failureVersion } = await getTelemetryFailureDetails({ + telemetrySavedObject, + }); return { telemetryOptIn: getTelemetryOptIn({ @@ -65,6 +75,8 @@ export class FetcherTask { configTelemetrySendUsageFrom, }), telemetryUrl, + failureCount, + failureVersion, }; }; @@ -72,11 +84,31 @@ export class FetcherTask { const internalRepository = this.getInternalRepository(); this.lastReported = Date.now(); updateTelemetrySavedObject(internalRepository, { + reportFailureCount: 0, lastReported: this.lastReported, }); }; - private shouldSendReport = ({ telemetryOptIn, telemetrySendUsageFrom }: any) => { + private updateReportFailure = async ({ failureCount }: { failureCount: number }) => { + const internalRepository = this.getInternalRepository(); + + updateTelemetrySavedObject(internalRepository, { + reportFailureCount: failureCount + 1, + reportFailureVersion: this.currentVersion, + }); + }; + + private shouldSendReport = ({ + telemetryOptIn, + telemetrySendUsageFrom, + reportFailureCount, + currentVersion, + reportFailureVersion, + }: any) => { + if (reportFailureCount > 2 && reportFailureVersion === currentVersion) { + return false; + } + if (telemetryOptIn && telemetrySendUsageFrom === 'server') { if (!this.lastReported || Date.now() - this.lastReported > REPORT_INTERVAL_MS) { return true; @@ -98,6 +130,14 @@ export class FetcherTask { private sendTelemetry = async (url: string, cluster: any): Promise => { this.server.log(['debug', 'telemetry', 'fetcher'], `Sending usage stats.`); + /** + * send OPTIONS before sending usage data. + * OPTIONS is less intrusive as it does not contain any payload and is used here to check if the endpoint is reachable. + */ + await fetch(url, { + method: 'options', + }); + await fetch(url, { method: 'post', body: cluster, @@ -108,21 +148,23 @@ export class FetcherTask { if (this.isSending) { return; } - try { - const telemetryConfig = await this.getCurrentConfigs(); - if (!this.shouldSendReport(telemetryConfig)) { - return; - } + const telemetryConfig = await this.getCurrentConfigs(); + if (!this.shouldSendReport(telemetryConfig)) { + return; + } - // mark that we are working so future requests are ignored until we're done + try { this.isSending = true; const clusters = await this.fetchTelemetry(); + const { telemetryUrl } = telemetryConfig; for (const cluster of clusters) { - await this.sendTelemetry(telemetryConfig.telemetryUrl, cluster); + await this.sendTelemetry(telemetryUrl, cluster); } await this.updateLastReported(); } catch (err) { + await this.updateReportFailure(telemetryConfig); + this.server.log( ['warning', 'telemetry', 'fetcher'], `Error sending telemetry usage data: ${err}` @@ -132,8 +174,12 @@ export class FetcherTask { }; public start = () => { - this.intervalId = setInterval(() => this.sendIfDue(), this.checkDurationMs); + setTimeout(() => { + this.sendIfDue(); + this.intervalId = setInterval(() => this.sendIfDue(), this.checkIntervalMs); + }, this.initialCheckDelayMs); }; + public stop = () => { if (this.intervalId) { clearInterval(this.intervalId); diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts new file mode 100644 index 00000000000000..c92696838e8e87 --- /dev/null +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { getTelemetryFailureDetails } from './get_telemetry_failure_details'; + +describe('getTelemetryFailureDetails: get details about server usage fetcher failures', () => { + it('returns `failureCount: 0` and `failureVersion: undefined` when telemetry does not have any custom configs in saved Object', () => { + const telemetrySavedObject = null; + const failureDetails = getTelemetryFailureDetails({ telemetrySavedObject }); + expect(failureDetails).toStrictEqual({ + failureVersion: undefined, + failureCount: 0, + }); + }); + + it('returns telemetryFailureCount and reportFailureVersion from telemetry saved Object', () => { + const telemetrySavedObject = { + reportFailureCount: 12, + reportFailureVersion: '8.0.0', + }; + const failureDetails = getTelemetryFailureDetails({ telemetrySavedObject }); + expect(failureDetails).toStrictEqual({ + failureVersion: '8.0.0', + failureCount: 12, + }); + }); + + it('returns `failureCount: 0` on malformed reportFailureCount telemetry saved Object', () => { + const failureVersion = '8.0.0'; + expect( + getTelemetryFailureDetails({ + telemetrySavedObject: { + reportFailureCount: null, + reportFailureVersion: failureVersion, + } as any, + }) + ).toStrictEqual({ failureVersion, failureCount: 0 }); + expect( + getTelemetryFailureDetails({ + telemetrySavedObject: { + reportFailureCount: undefined, + reportFailureVersion: failureVersion, + }, + }) + ).toStrictEqual({ failureVersion, failureCount: 0 }); + expect( + getTelemetryFailureDetails({ + telemetrySavedObject: { + reportFailureCount: 'not_a_number', + reportFailureVersion: failureVersion, + } as any, + }) + ).toStrictEqual({ failureVersion, failureCount: 0 }); + }); + + it('returns `failureVersion: undefined` on malformed reportFailureCount telemetry saved Object', () => { + const failureCount = 0; + expect( + getTelemetryFailureDetails({ + telemetrySavedObject: { + reportFailureVersion: null, + reportFailureCount: failureCount, + } as any, + }) + ).toStrictEqual({ failureCount, failureVersion: undefined }); + expect( + getTelemetryFailureDetails({ + telemetrySavedObject: { reportFailureVersion: undefined, reportFailureCount: failureCount }, + }) + ).toStrictEqual({ failureCount, failureVersion: undefined }); + expect( + getTelemetryFailureDetails({ + telemetrySavedObject: { + reportFailureVersion: 123, + reportFailureCount: failureCount, + } as any, + }) + ).toStrictEqual({ failureCount, failureVersion: undefined }); + }); +}); diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts new file mode 100644 index 00000000000000..2952fa96a5cf38 --- /dev/null +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts @@ -0,0 +1,45 @@ +/* + * 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 { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object'; + +interface GetTelemetryFailureDetailsConfig { + telemetrySavedObject: TelemetrySavedObject; +} + +export interface TelemetryFailureDetails { + failureCount: number; + failureVersion?: string; +} + +export function getTelemetryFailureDetails({ + telemetrySavedObject, +}: GetTelemetryFailureDetailsConfig): TelemetryFailureDetails { + if (!telemetrySavedObject) { + return { + failureVersion: undefined, + failureCount: 0, + }; + } + const { reportFailureCount, reportFailureVersion } = telemetrySavedObject; + + return { + failureCount: typeof reportFailureCount === 'number' ? reportFailureCount : 0, + failureVersion: typeof reportFailureVersion === 'string' ? reportFailureVersion : undefined, + }; +} diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts index ab30dac1c36660..bf9855ce7538e3 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts @@ -21,3 +21,7 @@ export { replaceTelemetryInjectedVars } from './replace_injected_vars'; export { getTelemetryOptIn } from './get_telemetry_opt_in'; export { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from'; export { getTelemetryAllowChangingOptInStatus } from './get_telemetry_allow_changing_opt_in_status'; +export { + getTelemetryFailureDetails, + TelemetryFailureDetails, +} from './get_telemetry_failure_details'; diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts b/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts index b9ba2ce5573c3c..f1735d1bb28669 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts @@ -27,4 +27,6 @@ export interface TelemetrySavedObjectAttributes { lastReported?: number; telemetryAllowChangingOptInStatus?: boolean; userHasSeenNotice?: boolean; + reportFailureCount?: number; + reportFailureVersion?: string; } diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index 7ba51cacd1949c..9cfb4ca1ec3952 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -54,9 +54,6 @@ export class TelemetryPlugin implements Plugin Date: Mon, 24 Feb 2020 13:24:46 -0600 Subject: [PATCH 10/21] Show blank lines instead of N/A on service map popovers (#57014) * Show blank lines instead of N/A on service map popovers Because RUM agents never show CPU or memory usage, we don't want to always show the metric with N/A. If a metric is null, just hide the lines. Break up the display and fetching components and update the popover stories to show the list. Update some types. * Fix metric typings --- .../app/ServiceMap/Popover/Contents.tsx | 4 +- .../ServiceMap/Popover/Popover.stories.tsx | 86 +++++++++-------- .../Popover/ServiceMetricFetcher.tsx | 41 +++++++++ .../ServiceMap/Popover/ServiceMetricList.tsx | 92 ++++++------------- x-pack/plugins/apm/common/service_map.ts | 9 ++ .../get_service_map_service_node_info.ts | 31 ++++--- 6 files changed, 147 insertions(+), 116 deletions(-) create mode 100644 x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx index 378ad9509c2170..f1c53673c87552 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx @@ -14,7 +14,7 @@ import cytoscape from 'cytoscape'; import React from 'react'; import { Buttons } from './Buttons'; import { Info } from './Info'; -import { ServiceMetricList } from './ServiceMetricList'; +import { ServiceMetricFetcher } from './ServiceMetricFetcher'; const popoverMinWidth = 280; @@ -49,7 +49,7 @@ export function Contents({ {isService ? ( - + ) : ( )} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx index b26488c5ef7de9..e5962afd76eb8d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx @@ -6,44 +6,50 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; -import { - ApmPluginContext, - ApmPluginContextValue -} from '../../../../context/ApmPluginContext'; -import { Contents } from './Contents'; +import { ServiceMetricList } from './ServiceMetricList'; -const selectedNodeData = { - id: 'opbeans-node', - label: 'opbeans-node', - href: - '#/services/opbeans-node/service-map?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0', - agentName: 'nodejs', - type: 'service' -}; - -storiesOf('app/ServiceMap/Popover/Contents', module).add( - 'example', - () => { - return ( - - {}} - selectedNodeServiceName="opbeans-node" - /> - - ); - }, - { - info: { - propTablesExclude: [ApmPluginContext.Provider], - source: false - } - } -); +storiesOf('app/ServiceMap/Popover/ServiceMetricList', module) + .add('example', () => ( + + )) + .add('loading', () => ( + + )) + .add('some null values', () => ( + + )) + .add('all null values', () => ( + + )); diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx new file mode 100644 index 00000000000000..b0a5e892b5a7e7 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx @@ -0,0 +1,41 @@ +/* + * 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 React from 'react'; +import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/common/service_map'; +import { useFetcher } from '../../../../hooks/useFetcher'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { ServiceMetricList } from './ServiceMetricList'; + +interface ServiceMetricFetcherProps { + serviceName: string; +} + +export function ServiceMetricFetcher({ + serviceName +}: ServiceMetricFetcherProps) { + const { + urlParams: { start, end, environment } + } = useUrlParams(); + + const { data = {} as ServiceNodeMetrics, status } = useFetcher( + callApmApi => { + if (serviceName && start && end) { + return callApmApi({ + pathname: '/api/apm/service-map/service/{serviceName}', + params: { path: { serviceName }, query: { start, end, environment } } + }); + } + }, + [serviceName, start, end, environment], + { + preservePreviousData: false + } + ); + const isLoading = status === 'loading'; + + return ; +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx index e91eb5e006d823..50ce918ea7037c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx @@ -5,26 +5,23 @@ */ import { + EuiBadge, EuiFlexGroup, - EuiLoadingSpinner, EuiFlexItem, - EuiBadge + EuiLoadingSpinner } from '@elastic/eui'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import { isNumber } from 'lodash'; import React from 'react'; import styled from 'styled-components'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/server/lib/service_map/get_service_map_service_node_info'; +import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/common/service_map'; import { asDuration, asPercent, toMicroseconds, tpmUnit } from '../../../../utils/formatters'; -import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { useFetcher } from '../../../../hooks/useFetcher'; function LoadingSpinner() { return ( @@ -51,53 +48,19 @@ const ItemDescription = styled('td')` text-align: right; `; -const na = i18n.translate('xpack.apm.serviceMap.NotAvailableMetric', { - defaultMessage: 'N/A' -}); - -interface MetricListProps { - serviceName: string; +interface ServiceMetricListProps extends ServiceNodeMetrics { + isLoading: boolean; } -export function ServiceMetricList({ serviceName }: MetricListProps) { - const { - urlParams: { start, end, environment } - } = useUrlParams(); - - const { data = {} as ServiceNodeMetrics, status } = useFetcher( - callApmApi => { - if (serviceName && start && end) { - return callApmApi({ - pathname: '/api/apm/service-map/service/{serviceName}', - params: { - path: { - serviceName - }, - query: { - start, - end, - environment - } - } - }); - } - }, - [serviceName, start, end, environment], - { - preservePreviousData: false - } - ); - - const { - avgTransactionDuration, - avgRequestsPerMinute, - avgErrorsPerMinute, - avgCpuUsage, - avgMemoryUsage, - numInstances - } = data; - const isLoading = status === 'loading'; - +export function ServiceMetricList({ + avgTransactionDuration, + avgRequestsPerMinute, + avgErrorsPerMinute, + avgCpuUsage, + avgMemoryUsage, + numInstances, + isLoading +}: ServiceMetricListProps) { const listItems = [ { title: i18n.translate( @@ -108,7 +71,7 @@ export function ServiceMetricList({ serviceName }: MetricListProps) { ), description: isNumber(avgTransactionDuration) ? asDuration(toMicroseconds(avgTransactionDuration, 'milliseconds')) - : na + : null }, { title: i18n.translate( @@ -119,7 +82,7 @@ export function ServiceMetricList({ serviceName }: MetricListProps) { ), description: isNumber(avgRequestsPerMinute) ? `${avgRequestsPerMinute.toFixed(2)} ${tpmUnit('request')}` - : na + : null }, { title: i18n.translate( @@ -128,13 +91,13 @@ export function ServiceMetricList({ serviceName }: MetricListProps) { defaultMessage: 'Errors per minute (avg.)' } ), - description: avgErrorsPerMinute?.toFixed(2) ?? na + description: avgErrorsPerMinute?.toFixed(2) }, { title: i18n.translate('xpack.apm.serviceMap.avgCpuUsagePopoverMetric', { defaultMessage: 'CPU usage (avg.)' }), - description: isNumber(avgCpuUsage) ? asPercent(avgCpuUsage, 1) : na + description: isNumber(avgCpuUsage) ? asPercent(avgCpuUsage, 1) : null }, { title: i18n.translate( @@ -143,7 +106,9 @@ export function ServiceMetricList({ serviceName }: MetricListProps) { defaultMessage: 'Memory usage (avg.)' } ), - description: isNumber(avgMemoryUsage) ? asPercent(avgMemoryUsage, 1) : na + description: isNumber(avgMemoryUsage) + ? asPercent(avgMemoryUsage, 1) + : null } ]; return isLoading ? ( @@ -165,12 +130,15 @@ export function ServiceMetricList({ serviceName }: MetricListProps) { - {listItems.map(({ title, description }) => ( - - {title} - {description} - - ))} + {listItems.map( + ({ title, description }) => + description && ( + + {title} + {description} + + ) + )}
diff --git a/x-pack/plugins/apm/common/service_map.ts b/x-pack/plugins/apm/common/service_map.ts index fbaa489c450399..528aec2f70ad98 100644 --- a/x-pack/plugins/apm/common/service_map.ts +++ b/x-pack/plugins/apm/common/service_map.ts @@ -21,3 +21,12 @@ export interface Connection { source: ConnectionNode; destination: ConnectionNode; } + +export interface ServiceNodeMetrics { + numInstances: number; + avgMemoryUsage: number | null; + avgCpuUsage: number | null; + avgTransactionDuration: number | null; + avgRequestsPerMinute: number | null; + avgErrorsPerMinute: number | null; +} diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts index 6c4d540103ceca..0fe825e8ace359 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts @@ -18,7 +18,6 @@ import { SERVICE_NODE_NAME } from '../../../common/elasticsearch_fieldnames'; import { percentMemoryUsedScript } from '../metrics/by_agent/shared/memory'; -import { PromiseReturnType } from '../../../typings/common'; interface Options { setup: Setup & SetupTimeRange; @@ -32,10 +31,6 @@ interface TaskParameters { filter: ESFilter[]; } -export type ServiceNodeMetrics = PromiseReturnType< - typeof getServiceMapServiceNodeInfo ->; - export async function getServiceMapServiceNodeInfo({ serviceName, environment, @@ -112,7 +107,10 @@ async function getTransactionMetrics({ setup, filter, minutes -}: TaskParameters) { +}: TaskParameters): Promise<{ + avgTransactionDuration: number | null; + avgRequestsPerMinute: number | null; +}> { const { indices, client } = setup; const response = await client.search({ @@ -140,13 +138,16 @@ async function getTransactionMetrics({ }); return { - avgTransactionDuration: response.aggregations?.duration.value, + avgTransactionDuration: response.aggregations?.duration.value ?? null, avgRequestsPerMinute: response.hits.total.value > 0 ? response.hits.total.value / minutes : null }; } -async function getCpuMetrics({ setup, filter }: TaskParameters) { +async function getCpuMetrics({ + setup, + filter +}: TaskParameters): Promise<{ avgCpuUsage: number | null }> { const { indices, client } = setup; const response = await client.search({ @@ -180,11 +181,14 @@ async function getCpuMetrics({ setup, filter }: TaskParameters) { }); return { - avgCpuUsage: response.aggregations?.avgCpuUsage.value + avgCpuUsage: response.aggregations?.avgCpuUsage.value ?? null }; } -async function getMemoryMetrics({ setup, filter }: TaskParameters) { +async function getMemoryMetrics({ + setup, + filter +}: TaskParameters): Promise<{ avgMemoryUsage: number | null }> { const { client, indices } = setup; const response = await client.search({ index: indices['apm_oss.metricsIndices'], @@ -221,11 +225,14 @@ async function getMemoryMetrics({ setup, filter }: TaskParameters) { }); return { - avgMemoryUsage: response.aggregations?.avgMemoryUsage.value + avgMemoryUsage: response.aggregations?.avgMemoryUsage.value ?? null }; } -async function getNumInstances({ setup, filter }: TaskParameters) { +async function getNumInstances({ + setup, + filter +}: TaskParameters): Promise<{ numInstances: number }> { const { client, indices } = setup; const response = await client.search({ index: indices['apm_oss.transactionIndices'], From 858fe2e9251e748bfbcd85623f22d9c1eac71076 Mon Sep 17 00:00:00 2001 From: Maggie Ghamry <46542915+maggieghamry@users.noreply.github.com> Date: Mon, 24 Feb 2020 14:44:19 -0500 Subject: [PATCH 11/21] [Canvas] Toggles footer editable controls when you turn off edit mode #52786 (#58044) * Bug fix update update to toggle footer edit controls "off" when locking edit controls. * Update to toggle Update to toggle, so that only the "Expression Editor" and tray are hidden when locking controls. * Update to toggle logic Added canUserWrite property, and added a condition to ensure the "page" tray still shows once the Expression editor is locked * Update to property definition Update to consolidate isWriteable and canUserWrite into one variable instead of two. * Adding Test (in progress) code Adding Toolbar test code (so far) - needs to be completed to address nested compononet storybook issue * Adding issue link Adding issue link in TODO comments Co-authored-by: Elastic Machine --- .../toolbar/__examples__/toolbar.stories.tsx | 39 +++++++++++++++++++ .../canvas/public/components/toolbar/index.js | 3 ++ .../public/components/toolbar/toolbar.tsx | 11 +++++- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx new file mode 100644 index 00000000000000..5907c932ddabb1 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/* + TODO: uncomment and fix this test to address storybook errors as a result of nested component dependencies - https://github.com/elastic/kibana/issues/58289 + */ + +/* +import { action } from '@storybook/addon-actions'; +import { storiesOf } from '@storybook/react'; +import React from 'react'; +import { Toolbar } from '../toolbar'; + +storiesOf('components/Toolbar', module) + .addDecorator(story => ( +
+ {story()} +
+ )) + .add('with null metric', () => ( + + )); +*/ diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js b/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js index c834304739a4c0..294a44ba0415a6 100644 --- a/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js @@ -7,12 +7,14 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { pure, compose, withState, getContext, withHandlers } from 'recompose'; +import { canUserWrite } from '../../state/selectors/app'; import { getWorkpad, getWorkpadName, getSelectedPageIndex, getSelectedElement, + isWriteable, } from '../../state/selectors/workpad'; import { Toolbar as Component } from './toolbar'; @@ -23,6 +25,7 @@ const mapStateToProps = state => ({ totalPages: getWorkpad(state).pages.length, selectedPageNumber: getSelectedPageIndex(state) + 1, selectedElement: getSelectedElement(state), + isWriteable: isWriteable(state) && canUserWrite(state), }); export const Toolbar = compose( diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx b/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx index 089f021ccdc325..0f8204e6bc261c 100644 --- a/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx @@ -39,7 +39,8 @@ enum TrayType { interface Props { workpadName: string; - + isWriteable: boolean; + canUserWrite: boolean; tray: TrayType | null; setTray: (tray: TrayType | null) => void; @@ -66,12 +67,17 @@ export const Toolbar = (props: Props) => { totalPages, showWorkpadManager, setShowWorkpadManager, + isWriteable, } = props; const elementIsSelected = Boolean(selectedElement); const done = () => setTray(null); + if (!isWriteable && tray === TrayType.expression) { + done(); + } + const showHideTray = (exp: TrayType) => { if (tray && tray === exp) { return done(); @@ -135,7 +141,7 @@ export const Toolbar = (props: Props) => { />
- {elementIsSelected && ( + {elementIsSelected && isWriteable && ( Date: Mon, 24 Feb 2020 20:09:38 +0000 Subject: [PATCH 12/21] chore(NA): remove empty filter check from thread loader pool config getter (#58385) --- src/optimize/base_optimizer.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/optimize/base_optimizer.js b/src/optimize/base_optimizer.js index a833204eaa0e28..a94f251c292f9a 100644 --- a/src/optimize/base_optimizer.js +++ b/src/optimize/base_optimizer.js @@ -152,11 +152,7 @@ export default class BaseOptimizer { getThreadLoaderPoolConfig() { // Calculate the node options from the NODE_OPTIONS env var - const parsedNodeOptions = process.env.NODE_OPTIONS - ? // thread-loader could not receive empty string as options - // or it would break that's why we need to filter here - process.env.NODE_OPTIONS.split(/\s/).filter(opt => !!opt) - : []; + const parsedNodeOptions = process.env.NODE_OPTIONS ? process.env.NODE_OPTIONS.split(/\s/) : []; return { name: 'optimizer-thread-loader-main-pool', From 77fe83e7db27b410cd1eb63084c22ca7bb32d278 Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Mon, 24 Feb 2020 13:04:30 -0800 Subject: [PATCH 13/21] Remove restriction that route must start with `/api` to use api authorization (#58351) Co-authored-by: Elastic Machine --- .../authorization/api_authorization.test.ts | 36 +++++-------------- .../server/authorization/api_authorization.ts | 4 +-- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/security/server/authorization/api_authorization.test.ts b/x-pack/plugins/security/server/authorization/api_authorization.test.ts index a5902f251b0829..409f998cfe8d2e 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.test.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.test.ts @@ -15,27 +15,7 @@ import { import { authorizationMock } from './index.mock'; describe('initAPIAuthorization', () => { - test(`route that doesn't start with "/api/" continues`, async () => { - const mockHTTPSetup = coreMock.createSetup().http; - initAPIAuthorization( - mockHTTPSetup, - authorizationMock.create(), - loggingServiceMock.create().get() - ); - - const [[postAuthHandler]] = mockHTTPSetup.registerOnPostAuth.mock.calls; - - const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', path: '/app/foo' }); - const mockResponse = httpServerMock.createResponseFactory(); - const mockPostAuthToolkit = httpServiceMock.createOnPostAuthToolkit(); - - await postAuthHandler(mockRequest, mockResponse, mockPostAuthToolkit); - - expect(mockResponse.notFound).not.toHaveBeenCalled(); - expect(mockPostAuthToolkit.next).toHaveBeenCalledTimes(1); - }); - - test(`protected route that starts with "/api/", but "mode.useRbacForRequest()" returns false continues`, async () => { + test(`protected route when "mode.useRbacForRequest()" returns false continues`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create(); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -44,7 +24,7 @@ describe('initAPIAuthorization', () => { const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', routeTags: ['access:foo'], }); const mockResponse = httpServerMock.createResponseFactory(); @@ -59,7 +39,7 @@ describe('initAPIAuthorization', () => { expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); - test(`unprotected route that starts with "/api/", but "mode.useRbacForRequest()" returns true continues`, async () => { + test(`unprotected route when "mode.useRbacForRequest()" returns true continues`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create(); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -68,7 +48,7 @@ describe('initAPIAuthorization', () => { const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', routeTags: ['not-access:foo'], }); const mockResponse = httpServerMock.createResponseFactory(); @@ -83,7 +63,7 @@ describe('initAPIAuthorization', () => { expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); - test(`protected route that starts with "/api/", "mode.useRbacForRequest()" returns true and user is authorized continues`, async () => { + test(`protected route when "mode.useRbacForRequest()" returns true and user is authorized continues`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create({ version: '1.0.0-zeta1' }); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -93,7 +73,7 @@ describe('initAPIAuthorization', () => { const headers = { authorization: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', headers, routeTags: ['access:foo'], }); @@ -118,7 +98,7 @@ describe('initAPIAuthorization', () => { expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); - test(`protected route that starts with "/api/", "mode.useRbacForRequest()" returns true and user isn't authorized responds with a 404`, async () => { + test(`protected route when "mode.useRbacForRequest()" returns true and user isn't authorized responds with a 404`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create({ version: '1.0.0-zeta1' }); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -128,7 +108,7 @@ describe('initAPIAuthorization', () => { const headers = { authorization: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', headers, routeTags: ['access:foo'], }); diff --git a/x-pack/plugins/security/server/authorization/api_authorization.ts b/x-pack/plugins/security/server/authorization/api_authorization.ts index b280cc74c230f5..cc672fbc69e068 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.ts @@ -13,8 +13,8 @@ export function initAPIAuthorization( logger: Logger ) { http.registerOnPostAuth(async (request, response, toolkit) => { - // if the api doesn't start with "/api/" or we aren't using RBAC for this request, just continue - if (!request.url.path!.startsWith('/api/') || !mode.useRbacForRequest(request)) { + // if we aren't using RBAC for this request, just continue + if (!mode.useRbacForRequest(request)) { return toolkit.next(); } From 7e087633d26dfebe5cf262bbfde2cd4c29770464 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 24 Feb 2020 14:44:03 -0700 Subject: [PATCH 14/21] Remove unused indexPattern:fieldMapping:lookBack advanced setting (#58147) * Remove unused indexPattern:fieldMapping:lookBack advanced setting * Remove unused translations Co-authored-by: Elastic Machine --- docs/management/advanced-options.asciidoc | 2 -- src/legacy/core_plugins/kibana/ui_setting_defaults.js | 11 ----------- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 4 files changed, 17 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 80c9053dc5ae6f..9d4052bbd0156a 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -62,8 +62,6 @@ mentioned use "\_default_". `histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values when necessary. `history:limit`:: In fields that have history, such as query inputs, show this many recent values. -`indexPattern:fieldMapping:lookBack`:: For index patterns containing timestamps in their names, -look for this many recent matching patterns from which to query the field mapping. `indexPattern:placeholder`:: The default placeholder value to use in Management > Index Patterns > Create Index Pattern. `metaFields`:: Fields that exist outside of `_source`. Kibana merges these fields into the document when displaying it. diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index f92694eabe58d1..c0628b72c2ce7a 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -690,17 +690,6 @@ export function getUiSettingDefaults() { 'The maximum height that a cell in a table should occupy. Set to 0 to disable truncation', }), }, - 'indexPattern:fieldMapping:lookBack': { - name: i18n.translate('kbn.advancedSettings.indexPattern.recentMatchingTitle', { - defaultMessage: 'Recent matching patterns', - }), - value: 5, - description: i18n.translate('kbn.advancedSettings.indexPattern.recentMatchingText', { - defaultMessage: - 'For index patterns containing timestamps in their names, look for this many recent matching ' + - 'patterns from which to query the field mapping', - }), - }, 'format:defaultTypeMap': { name: i18n.translate('kbn.advancedSettings.format.defaultTypeMapTitle', { defaultMessage: 'Field type format name', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 78bb39dd22dea1..dc97a96d4fcbab 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -878,8 +878,6 @@ "kbn.advancedSettings.histogram.maxBarsTitle": "最高バー数", "kbn.advancedSettings.historyLimitText": "履歴があるフィールド (例: クエリインプット) に個の数の最近の値が表示されます", "kbn.advancedSettings.historyLimitTitle": "履歴制限数", - "kbn.advancedSettings.indexPattern.recentMatchingText": "名前にタイムスタンプが含まれているインデックスパターンで、フィールドマッチングをクエリする最近の一致したパターンが、この数検索されます", - "kbn.advancedSettings.indexPattern.recentMatchingTitle": "最近一致したパターン", "kbn.advancedSettings.indexPatternPlaceholderText": "「管理 > インデックスパターン > インデックスパターンを作成」で使用される「インデックスパターン名」フィールドのプレースホルダーです。", "kbn.advancedSettings.indexPatternPlaceholderTitle": "インデックスパターンのプレースホルダー", "kbn.advancedSettings.maxBucketsText": "1 つのデータソースが返せるバケットの最大数です", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index fc9dacf0e50f7b..2532cdb0c4d074 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -878,8 +878,6 @@ "kbn.advancedSettings.histogram.maxBarsTitle": "最大条形数", "kbn.advancedSettings.historyLimitText": "在具有历史记录(例如查询输入)的字段中,显示此数目的最近值", "kbn.advancedSettings.historyLimitTitle": "历史记录限制", - "kbn.advancedSettings.indexPattern.recentMatchingText": "对于名称中包含时间戳的索引模式,寻找此数目的最近匹配模式,以从其中查询字段映射", - "kbn.advancedSettings.indexPattern.recentMatchingTitle": "最近匹配模式", "kbn.advancedSettings.indexPatternPlaceholderText": "在“管理 > 索引模式 > 创建索引模式”中“索引模式名称”的占位符。", "kbn.advancedSettings.indexPatternPlaceholderTitle": "索引模式占位符", "kbn.advancedSettings.maxBucketsText": "单个数据源可以返回的最大存储桶数目", From 13eacb51f0fd030f123c05e68c545bdb99f6ac70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 24 Feb 2020 23:43:40 +0100 Subject: [PATCH 15/21] [APM] Stabilize agent configuration API (#57767) --- docs/apm/api.asciidoc | 260 ++++++++++++++++++ docs/apm/index.asciidoc | 2 + .../AddEditFlyout/DeleteButton.tsx | 10 +- .../AddEditFlyout/index.tsx | 2 +- .../AddEditFlyout/saveConfig.ts | 30 +- x-pack/legacy/plugins/apm/readme.md | 22 ++ .../index.test.ts | 41 +++ .../agent_configuration_intake_rt/index.ts | 26 ++ .../apm/server/lib/helpers/es_client.ts | 7 +- .../lib/services/get_service_node_metadata.ts | 4 +- .../__snapshots__/queries.test.ts.snap | 234 ++++++++++------ .../configuration_types.d.ts | 19 +- .../create_or_update_configuration.ts | 22 +- .../find_exact_configuration.ts | 46 ++++ .../get_agent_name_by_service.ts | 6 +- .../agent_configuration/queries.test.ts | 146 ++++++---- .../{search.ts => search_configurations.ts} | 35 ++- .../apm/server/routes/create_apm_api.ts | 6 +- .../routes/settings/agent_configuration.ts | 137 ++++----- .../apis/apm/agent_configuration.ts | 163 ++++++----- .../apis/apm/feature_controls.ts | 42 +-- x-pack/test/api_integration/apis/apm/index.ts | 2 +- x-pack/test/functional/apps/apm/index.ts | 2 +- 23 files changed, 894 insertions(+), 370 deletions(-) create mode 100644 docs/apm/api.asciidoc create mode 100644 x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts create mode 100644 x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts create mode 100644 x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts rename x-pack/plugins/apm/server/lib/settings/agent_configuration/{search.ts => search_configurations.ts} (68%) diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc new file mode 100644 index 00000000000000..b520cc46bef8d3 --- /dev/null +++ b/docs/apm/api.asciidoc @@ -0,0 +1,260 @@ +[role="xpack"] +[[apm-api]] +== API + +Some APM app features are provided via a REST API: + +* <> + +TIP: Kibana provides additional <>, +and general information on <>. + +//// +******************************************************* +//// + +[[agent-config-api]] +=== Agent Configuration API + +The Agent configuration API allows you to fine-tune your APM agent configuration, +without needing to redeploy your application. + +The following Agent configuration APIs are available: + +* <> to create or update an Agent configuration +* <> to delete an Agent configuration. +* <> to list all Agent configurations. +* <> to search for an Agent configuration. + +//// +******************************************************* +//// + +[[apm-update-config]] +==== Create or update configuration + +[[apm-update-config-req]] +===== Request + +`PUT /api/apm/settings/agent-configuration` + +[[apm-update-config-req-body]] +===== Request body + +`service`:: +(required, object) Service identifying the configuration to create or update. + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service + +`settings`:: +(required) Key/value object with settings and their corresponding value. + +`agent_name`:: +(optional) The agent name is used by the UI to determine which settings to display. + + +[[apm-update-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +PUT /api/apm/settings/agent-configuration +{ + "service" : { + "name" : "frontend", + "environment" : "production" + }, + "settings" : { + "transaction_sample_rate" : 0.4, + "capture_body" : "off", + "transaction_max_spans" : 500 + }, + "agent_name": "nodejs" +} +-------------------------------------------------- + +//// +******************************************************* +//// + + +[[apm-delete-config]] +==== Delete configuration + +[[apm-delete-config-req]] +===== Request + +`DELETE /api/apm/settings/agent-configuration` + +[[apm-delete-config-req-body]] +===== Request body +`service`:: +(required, object) Service identifying the configuration to delete + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service + + +[[apm-delete-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +DELETE /api/apm/settings/agent-configuration +{ + "service" : { + "name" : "frontend", + "environment": "production" + } +} +-------------------------------------------------- + +//// +******************************************************* +//// + + +[[apm-list-config]] +==== List configuration + + +[[apm-list-config-req]] +===== Request + +`GET /api/apm/settings/agent-configuration` + +[[apm-list-config-body]] +===== Response body + +[source,js] +-------------------------------------------------- +[ + { + "agent_name": "go", + "service": { + "name": "opbeans-go", + "environment": "production" + }, + "settings": { + "transaction_sample_rate": 1, + "capture_body": "off", + "transaction_max_spans": 200 + }, + "@timestamp": 1581934104843, + "applied_by_agent": false, + "etag": "1e58c178efeebae15c25c539da740d21dee422fc" + }, + { + "agent_name": "go", + "service": { + "name": "opbeans-go" + }, + "settings": { + "transaction_sample_rate": 1, + "capture_body": "off", + "transaction_max_spans": 300 + }, + "@timestamp": 1581934111727, + "applied_by_agent": false, + "etag": "3eed916d3db434d9fb7f039daa681c7a04539a64" + }, + { + "agent_name": "nodejs", + "service": { + "name": "frontend" + }, + "settings": { + "transaction_sample_rate": 1, + }, + "@timestamp": 1582031336265, + "applied_by_agent": false, + "etag": "5080ed25785b7b19f32713681e79f46996801a5b" + } +] +-------------------------------------------------- + +[[apm-list-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +GET /api/apm/settings/agent-configuration +-------------------------------------------------- + +//// +******************************************************* +//// + + +[[apm-search-config]] +==== Search configuration + +[[apm-search-config-req]] +===== Request + +`POST /api/apm/settings/agent-configuration/search` + +[[apm-search-config-req-body]] +===== Request body + +`service`:: +(required, object) Service identifying the configuration. + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service + +`etag`:: +(required) etag is sent by the agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`. + +[[apm-search-config-body]] +===== Response body + +[source,js] +-------------------------------------------------- +{ + "_index": ".apm-agent-configuration", + "_id": "CIaqXXABmQCdPphWj8EJ", + "_score": 2, + "_source": { + "agent_name": "nodejs", + "service": { + "name": "frontend" + }, + "settings": { + "transaction_sample_rate": 1, + }, + "@timestamp": 1582031336265, + "applied_by_agent": false, + "etag": "5080ed25785b7b19f32713681e79f46996801a5b" + } +} +-------------------------------------------------- + +[[apm-search-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +POST /api/apm/settings/agent-configuration/search +{ + "etag" : "1e58c178efeebae15c25c539da740d21dee422fc", + "service" : { + "name" : "frontend", + "environment": "production" + } +} +-------------------------------------------------- + +//// +******************************************************* +//// diff --git a/docs/apm/index.asciidoc b/docs/apm/index.asciidoc index 7eb7278cf03581..d3f0dc5b7f11f2 100644 --- a/docs/apm/index.asciidoc +++ b/docs/apm/index.asciidoc @@ -24,3 +24,5 @@ include::getting-started.asciidoc[] include::bottlenecks.asciidoc[] include::using-the-apm-ui.asciidoc[] + +include::api.asciidoc[] diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx index 496147b02589bc..1564f1ae746a99 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx @@ -51,12 +51,18 @@ async function deleteConfig( ) { try { await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/{configurationId}', + pathname: '/api/apm/settings/agent-configuration', method: 'DELETE', params: { - path: { configurationId: selectedConfig.id } + body: { + service: { + name: selectedConfig.service.name, + environment: selectedConfig.service.environment + } + } } }); + toasts.addSuccess({ title: i18n.translate( 'xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededTitle', diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx index 653dedea733f26..c77617fbb424f0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx @@ -135,8 +135,8 @@ export function AddEditFlyout({ sampleRate, captureBody, transactionMaxSpans, - configurationId: selectedConfig ? selectedConfig.id : undefined, agentName, + isExistingConfig: Boolean(selectedConfig), toasts, trackApmEvent }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts index 19934cafb4694f..d36120a0547959 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts @@ -27,8 +27,8 @@ export async function saveConfig({ sampleRate, captureBody, transactionMaxSpans, - configurationId, agentName, + isExistingConfig, toasts, trackApmEvent }: { @@ -38,8 +38,8 @@ export async function saveConfig({ sampleRate: string; captureBody: string; transactionMaxSpans: string; - configurationId?: string; agentName?: string; + isExistingConfig: boolean; toasts: NotificationsStart['toasts']; trackApmEvent: UiTracker; }) { @@ -64,24 +64,14 @@ export async function saveConfig({ settings }; - if (configurationId) { - await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/{configurationId}', - method: 'PUT', - params: { - path: { configurationId }, - body: configuration - } - }); - } else { - await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/new', - method: 'POST', - params: { - body: configuration - } - }); - } + await callApmApi({ + pathname: '/api/apm/settings/agent-configuration', + method: 'PUT', + params: { + query: { overwrite: isExistingConfig }, + body: configuration + } + }); toasts.addSuccess({ title: i18n.translate( diff --git a/x-pack/legacy/plugins/apm/readme.md b/x-pack/legacy/plugins/apm/readme.md index 2106243d12aeaf..a513249c296db3 100644 --- a/x-pack/legacy/plugins/apm/readme.md +++ b/x-pack/legacy/plugins/apm/readme.md @@ -71,6 +71,28 @@ node scripts/jest.js plugins/apm --watch node scripts/jest.js plugins/apm --updateSnapshot ``` +### Functional tests + +**Start server** +`node scripts/functional_tests_server --config x-pack/test/functional/config.js` + +**Run tests** +`node scripts/functional_test_runner --config x-pack/test/functional/config.js --grep='APM specs'` + +APM tests are located in `x-pack/test/functional/apps/apm`. +For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme) + +### API integration tests + +**Start server** +`node scripts/functional_tests_server --config x-pack/test/api_integration/config.js` + +**Run tests** +`node scripts/functional_test_runner --config x-pack/test/api_integration/config.js --grep='APM specs'` + +APM tests are located in `x-pack/test/api_integration/apis/apm`. +For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme) + ### Linting _Note: Run the following commands from `kibana/`._ diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts new file mode 100644 index 00000000000000..4c9dc78eb41e9d --- /dev/null +++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts @@ -0,0 +1,41 @@ +/* + * 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 { agentConfigurationIntakeRt } from './index'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('agentConfigurationIntakeRt', () => { + it('is valid when required parameters are given', () => { + const config = { + service: {}, + settings: {} + }; + + expect(isConfigValid(config)).toBe(true); + }); + + it('is valid when required and optional parameters are given', () => { + const config = { + service: { name: 'my-service', environment: 'my-environment' }, + settings: { + transaction_sample_rate: 0.5, + capture_body: 'foo', + transaction_max_spans: 10 + } + }; + + expect(isConfigValid(config)).toBe(true); + }); + + it('is invalid when required parameters are not given', () => { + const config = {}; + expect(isConfigValid(config)).toBe(false); + }); +}); + +function isConfigValid(config: any) { + return isRight(agentConfigurationIntakeRt.decode(config)); +} diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts new file mode 100644 index 00000000000000..32a2832b5eaf33 --- /dev/null +++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts @@ -0,0 +1,26 @@ +/* + * 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 * as t from 'io-ts'; +import { transactionSampleRateRt } from '../transaction_sample_rate_rt'; +import { transactionMaxSpansRt } from '../transaction_max_spans_rt'; + +export const serviceRt = t.partial({ + name: t.string, + environment: t.string +}); + +export const agentConfigurationIntakeRt = t.intersection([ + t.partial({ agent_name: t.string }), + t.type({ + service: serviceRt, + settings: t.partial({ + transaction_sample_rate: transactionSampleRateRt, + capture_body: t.string, + transaction_max_spans: transactionMaxSpansRt + }) + }) +]); diff --git a/x-pack/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/plugins/apm/server/lib/helpers/es_client.ts index 8ada02d085631b..86eb1dba507f04 100644 --- a/x-pack/plugins/apm/server/lib/helpers/es_client.ts +++ b/x-pack/plugins/apm/server/lib/helpers/es_client.ts @@ -7,9 +7,10 @@ /* eslint-disable no-console */ import { IndexDocumentParams, - IndicesCreateParams, IndicesDeleteParams, - SearchParams + SearchParams, + IndicesCreateParams, + DeleteDocumentResponse } from 'elasticsearch'; import { cloneDeep, isString, merge, uniqueId } from 'lodash'; import { KibanaRequest } from 'src/core/server'; @@ -188,7 +189,7 @@ export function getESClient( index: (params: APMIndexDocumentParams) => { return withTime(() => callMethod('index', params)); }, - delete: (params: IndicesDeleteParams) => { + delete: (params: IndicesDeleteParams): Promise => { return withTime(() => callMethod('delete', params)); }, indicesCreate: (params: IndicesCreateParams) => { diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts index 7120d3bca6c259..ccd8b123e23e2a 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts @@ -58,8 +58,8 @@ export async function getServiceNodeMetadata({ const response = await client.search(query); return { - host: response.aggregations?.host.buckets[0].key || NOT_AVAILABLE_LABEL, + host: response.aggregations?.host.buckets[0]?.key || NOT_AVAILABLE_LABEL, containerId: - response.aggregations?.containerId.buckets[0].key || NOT_AVAILABLE_LABEL + response.aggregations?.containerId.buckets[0]?.key || NOT_AVAILABLE_LABEL }; } diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap index 542fdd99e26358..db34b4d5d20b5b 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap @@ -1,6 +1,90 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`agent configuration queries fetches all environments 1`] = ` +exports[`agent configuration queries findExactConfiguration find configuration by service.environment 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "service.name", + }, + }, + ], + }, + }, + Object { + "term": Object { + "service.environment": "bar", + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries findExactConfiguration find configuration by service.name 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "service.environment", + }, + }, + ], + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries findExactConfiguration find configuration by service.name and service.environment 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + Object { + "term": Object { + "service.environment": "bar", + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries getAllEnvironments fetches all environments 1`] = ` Object { "body": Object { "aggs": Object { @@ -41,14 +125,79 @@ Object { } `; -exports[`agent configuration queries fetches configurations 1`] = ` +exports[`agent configuration queries getExistingEnvironmentsForService fetches unavailable environments 1`] = ` +Object { + "body": Object { + "aggs": Object { + "environments": Object { + "terms": Object { + "field": "service.environment", + "missing": "ALL_OPTION_VALUE", + "size": 50, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + ], + }, + }, + "size": 0, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries getServiceNames fetches service names 1`] = ` +Object { + "body": Object { + "aggs": Object { + "services": Object { + "terms": Object { + "field": "service.name", + "size": 50, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "terms": Object { + "processor.event": Array [ + "transaction", + "error", + "metric", + ], + }, + }, + ], + }, + }, + "size": 0, + }, + "index": Array [ + "myIndex", + "myIndex", + "myIndex", + ], +} +`; + +exports[`agent configuration queries listConfigurations fetches configurations 1`] = ` Object { "index": "myIndex", "size": 200, } `; -exports[`agent configuration queries fetches filtered configurations with an environment 1`] = ` +exports[`agent configuration queries searchConfigurations fetches filtered configurations with an environment 1`] = ` Object { "body": Object { "query": Object { @@ -60,9 +209,7 @@ Object { "boost": 2, "filter": Object { "term": Object { - "service.name": Object { - "value": "foo", - }, + "service.name": "foo", }, }, }, @@ -72,9 +219,7 @@ Object { "boost": 1, "filter": Object { "term": Object { - "service.environment": Object { - "value": "bar", - }, + "service.environment": "bar", }, }, }, @@ -109,7 +254,7 @@ Object { } `; -exports[`agent configuration queries fetches filtered configurations without an environment 1`] = ` +exports[`agent configuration queries searchConfigurations fetches filtered configurations without an environment 1`] = ` Object { "body": Object { "query": Object { @@ -121,9 +266,7 @@ Object { "boost": 2, "filter": Object { "term": Object { - "service.name": Object { - "value": "foo", - }, + "service.name": "foo", }, }, }, @@ -157,68 +300,3 @@ Object { "index": "myIndex", } `; - -exports[`agent configuration queries fetches service names 1`] = ` -Object { - "body": Object { - "aggs": Object { - "services": Object { - "terms": Object { - "field": "service.name", - "size": 50, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "terms": Object { - "processor.event": Array [ - "transaction", - "error", - "metric", - ], - }, - }, - ], - }, - }, - "size": 0, - }, - "index": Array [ - "myIndex", - "myIndex", - "myIndex", - ], -} -`; - -exports[`agent configuration queries fetches unavailable environments 1`] = ` -Object { - "body": Object { - "aggs": Object { - "environments": Object { - "terms": Object { - "field": "service.environment", - "missing": "ALL_OPTION_VALUE", - "size": 50, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "foo", - }, - }, - ], - }, - }, - "size": 0, - }, - "index": "myIndex", -} -`; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts index ea8f50c90c1d3a..ddbe6892c54414 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts @@ -4,18 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface AgentConfiguration { +import t from 'io-ts'; +import { agentConfigurationIntakeRt } from '../../../../common/runtime_types/agent_configuration_intake_rt'; + +export type AgentConfigurationIntake = t.TypeOf< + typeof agentConfigurationIntakeRt +>; +export type AgentConfiguration = { '@timestamp': number; applied_by_agent?: boolean; etag?: string; agent_name?: string; - service: { - name?: string; - environment?: string; - }; - settings: { - transaction_sample_rate?: number; - capture_body?: string; - transaction_max_spans?: number; - }; -} +} & AgentConfigurationIntake; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts index 5a67f78de6f652..74fcc61dde863c 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts @@ -6,19 +6,19 @@ import hash from 'object-hash'; import { Setup } from '../../helpers/setup_request'; -import { AgentConfiguration } from './configuration_types'; +import { + AgentConfiguration, + AgentConfigurationIntake +} from './configuration_types'; import { APMIndexDocumentParams } from '../../helpers/es_client'; export async function createOrUpdateConfiguration({ configurationId, - configuration, + configurationIntake, setup }: { configurationId?: string; - configuration: Omit< - AgentConfiguration, - '@timestamp' | 'applied_by_agent' | 'etag' - >; + configurationIntake: AgentConfigurationIntake; setup: Setup; }) { const { internalClient, indices } = setup; @@ -27,15 +27,15 @@ export async function createOrUpdateConfiguration({ refresh: true, index: indices.apmAgentConfigurationIndex, body: { - agent_name: configuration.agent_name, + agent_name: configurationIntake.agent_name, service: { - name: configuration.service.name, - environment: configuration.service.environment + name: configurationIntake.service.name, + environment: configurationIntake.service.environment }, - settings: configuration.settings, + settings: configurationIntake.settings, '@timestamp': Date.now(), applied_by_agent: false, - etag: hash(configuration) + etag: hash(configurationIntake) } }; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts new file mode 100644 index 00000000000000..eea409882f8763 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts @@ -0,0 +1,46 @@ +/* + * 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 { + SERVICE_NAME, + SERVICE_ENVIRONMENT +} from '../../../../common/elasticsearch_fieldnames'; +import { Setup } from '../../helpers/setup_request'; +import { AgentConfiguration } from './configuration_types'; +import { ESSearchHit } from '../../../../typings/elasticsearch'; + +export async function findExactConfiguration({ + service, + setup +}: { + service: AgentConfiguration['service']; + setup: Setup; +}) { + const { internalClient, indices } = setup; + + const serviceNameFilter = service.name + ? { term: { [SERVICE_NAME]: service.name } } + : { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } }; + + const environmentFilter = service.environment + ? { term: { [SERVICE_ENVIRONMENT]: service.environment } } + : { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } }; + + const params = { + index: indices.apmAgentConfigurationIndex, + body: { + query: { + bool: { filter: [serviceNameFilter, environmentFilter] } + } + } + }; + + const resp = await internalClient.search( + params + ); + + return resp.hits.hits[0] as ESSearchHit | undefined; +} diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts index dccf8b110d082b..a9af1f6174fd51 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts @@ -48,8 +48,6 @@ export async function getAgentNameByService({ }; const { aggregations } = await client.search(params); - const agentName = aggregations?.agent_names.buckets[0].key as - | string - | undefined; - return { agentName }; + const agentName = aggregations?.agent_names.buckets[0]?.key; + return agentName as string | undefined; } diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts index a82d148781ad88..b951b7f350eed4 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts @@ -8,11 +8,12 @@ import { getAllEnvironments } from './get_environments/get_all_environments'; import { getExistingEnvironmentsForService } from './get_environments/get_existing_environments_for_service'; import { getServiceNames } from './get_service_names'; import { listConfigurations } from './list_configurations'; -import { searchConfigurations } from './search'; +import { searchConfigurations } from './search_configurations'; import { SearchParamsMock, inspectSearchParams } from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +import { findExactConfiguration } from './find_exact_configuration'; describe('agent configuration queries', () => { let mock: SearchParamsMock; @@ -21,68 +22,117 @@ describe('agent configuration queries', () => { mock.teardown(); }); - it('fetches all environments', async () => { - mock = await inspectSearchParams(setup => - getAllEnvironments({ - serviceName: 'foo', - setup - }) - ); + describe('getAllEnvironments', () => { + it('fetches all environments', async () => { + mock = await inspectSearchParams(setup => + getAllEnvironments({ + serviceName: 'foo', + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches unavailable environments', async () => { - mock = await inspectSearchParams(setup => - getExistingEnvironmentsForService({ - serviceName: 'foo', - setup - }) - ); + describe('getExistingEnvironmentsForService', () => { + it('fetches unavailable environments', async () => { + mock = await inspectSearchParams(setup => + getExistingEnvironmentsForService({ + serviceName: 'foo', + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches service names', async () => { - mock = await inspectSearchParams(setup => - getServiceNames({ - setup - }) - ); + describe('getServiceNames', () => { + it('fetches service names', async () => { + mock = await inspectSearchParams(setup => + getServiceNames({ + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches configurations', async () => { - mock = await inspectSearchParams(setup => - listConfigurations({ - setup - }) - ); + describe('listConfigurations', () => { + it('fetches configurations', async () => { + mock = await inspectSearchParams(setup => + listConfigurations({ + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches filtered configurations without an environment', async () => { - mock = await inspectSearchParams(setup => - searchConfigurations({ - serviceName: 'foo', - setup - }) - ); + describe('searchConfigurations', () => { + it('fetches filtered configurations without an environment', async () => { + mock = await inspectSearchParams(setup => + searchConfigurations({ + service: { + name: 'foo' + }, + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); + + it('fetches filtered configurations with an environment', async () => { + mock = await inspectSearchParams(setup => + searchConfigurations({ + service: { + name: 'foo', + environment: 'bar' + }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches filtered configurations with an environment', async () => { - mock = await inspectSearchParams(setup => - searchConfigurations({ - serviceName: 'foo', - environment: 'bar', - setup - }) - ); + describe('findExactConfiguration', () => { + it('find configuration by service.name', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { name: 'foo' }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); + + it('find configuration by service.environment', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { environment: 'bar' }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); + + it('find configuration by service.name and service.environment', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { name: 'foo', environment: 'bar' }, + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); }); diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts similarity index 68% rename from x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts index 766baead006b6e..9bbdc96a3a7974 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts @@ -12,29 +12,39 @@ import { Setup } from '../../helpers/setup_request'; import { AgentConfiguration } from './configuration_types'; export async function searchConfigurations({ - serviceName, - environment, + service, setup }: { - serviceName: string; - environment?: string; + service: AgentConfiguration['service']; setup: Setup; }) { const { internalClient, indices } = setup; - const environmentFilter = environment + + // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring). + // Additionally a boost has been added to service.name to ensure it scores higher. + // If there is tie between a config with a matching service.name and a config with a matching environment, the config that matches service.name wins + const serviceNameFilter = service.name + ? [ + { + constant_score: { + filter: { term: { [SERVICE_NAME]: service.name } }, + boost: 2 + } + } + ] + : []; + + const environmentFilter = service.environment ? [ { constant_score: { - filter: { term: { [SERVICE_ENVIRONMENT]: { value: environment } } }, + filter: { term: { [SERVICE_ENVIRONMENT]: service.environment } }, boost: 1 } } ] : []; - // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring) - // Additionally a boost has been added to service.name to ensure it scores higher - // if there is tie between a config with a matching service.name and a config with a matching environment const params = { index: indices.apmAgentConfigurationIndex, body: { @@ -42,12 +52,7 @@ export async function searchConfigurations({ bool: { minimum_should_match: 2, should: [ - { - constant_score: { - filter: { term: { [SERVICE_NAME]: { value: serviceName } } }, - boost: 2 - } - }, + ...serviceNameFilter, ...environmentFilter, { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } }, { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } } diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index f65e271389938a..21392edbb2c48f 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -23,11 +23,10 @@ import { import { agentConfigurationRoute, agentConfigurationSearchRoute, - createAgentConfigurationRoute, deleteAgentConfigurationRoute, listAgentConfigurationEnvironmentsRoute, listAgentConfigurationServicesRoute, - updateAgentConfigurationRoute, + createOrUpdateAgentConfigurationRoute, agentConfigurationAgentNameRoute } from './settings/agent_configuration'; import { @@ -83,11 +82,10 @@ const createApmApi = () => { .add(agentConfigurationAgentNameRoute) .add(agentConfigurationRoute) .add(agentConfigurationSearchRoute) - .add(createAgentConfigurationRoute) .add(deleteAgentConfigurationRoute) .add(listAgentConfigurationEnvironmentsRoute) .add(listAgentConfigurationServicesRoute) - .add(updateAgentConfigurationRoute) + .add(createOrUpdateAgentConfigurationRoute) // APM indices .add(apmIndexSettingsRoute) diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts index ddd6a270251310..83b845b1fc4365 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts @@ -9,15 +9,19 @@ import Boom from 'boom'; import { setupRequest } from '../../lib/helpers/setup_request'; import { getServiceNames } from '../../lib/settings/agent_configuration/get_service_names'; import { createOrUpdateConfiguration } from '../../lib/settings/agent_configuration/create_or_update_configuration'; -import { searchConfigurations } from '../../lib/settings/agent_configuration/search'; +import { searchConfigurations } from '../../lib/settings/agent_configuration/search_configurations'; +import { findExactConfiguration } from '../../lib/settings/agent_configuration/find_exact_configuration'; import { listConfigurations } from '../../lib/settings/agent_configuration/list_configurations'; import { getEnvironments } from '../../lib/settings/agent_configuration/get_environments'; import { deleteConfiguration } from '../../lib/settings/agent_configuration/delete_configuration'; import { createRoute } from '../create_route'; -import { transactionSampleRateRt } from '../../../common/runtime_types/transaction_sample_rate_rt'; -import { transactionMaxSpansRt } from '../../../common/runtime_types/transaction_max_spans_rt'; import { getAgentNameByService } from '../../lib/settings/agent_configuration/get_agent_name_by_service'; import { markAppliedByAgent } from '../../lib/settings/agent_configuration/mark_applied_by_agent'; +import { + serviceRt, + agentConfigurationIntakeRt +} from '../../../common/runtime_types/agent_configuration_intake_rt'; +import { jsonRt } from '../../../common/runtime_types/json_rt'; // get list of configurations export const agentConfigurationRoute = createRoute(core => ({ @@ -31,20 +35,34 @@ export const agentConfigurationRoute = createRoute(core => ({ // delete configuration export const deleteAgentConfigurationRoute = createRoute(() => ({ method: 'DELETE', - path: '/api/apm/settings/agent-configuration/{configurationId}', + path: '/api/apm/settings/agent-configuration', options: { tags: ['access:apm', 'access:apm_write'] }, params: { - path: t.type({ - configurationId: t.string + body: t.type({ + service: serviceRt }) }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const { configurationId } = context.params.path; + const { service } = context.params.body; + + const config = await findExactConfiguration({ service, setup }); + if (!config) { + context.logger.info( + `Config was not found for ${service.name}/${service.environment}` + ); + + throw Boom.notFound(); + } + + context.logger.info( + `Deleting config ${service.name}/${service.environment} (${config._id})` + ); + return await deleteConfiguration({ - configurationId, + configurationId: config._id, setup }); } @@ -62,23 +80,6 @@ export const listAgentConfigurationServicesRoute = createRoute(() => ({ } })); -const agentPayloadRt = t.intersection([ - t.partial({ agent_name: t.string }), - t.type({ - service: t.intersection([ - t.partial({ name: t.string }), - t.partial({ environment: t.string }) - ]) - }), - t.type({ - settings: t.intersection([ - t.partial({ transaction_sample_rate: transactionSampleRateRt }), - t.partial({ capture_body: t.string }), - t.partial({ transaction_max_spans: transactionMaxSpansRt }) - ]) - }) -]); - // get environments for service export const listAgentConfigurationEnvironmentsRoute = createRoute(() => ({ path: '/api/apm/settings/agent-configuration/environments', @@ -102,55 +103,47 @@ export const agentConfigurationAgentNameRoute = createRoute(() => ({ const setup = await setupRequest(context, request); const { serviceName } = context.params.query; const agentName = await getAgentNameByService({ serviceName, setup }); - return agentName; + return { agentName }; } })); -export const createAgentConfigurationRoute = createRoute(() => ({ - method: 'POST', - path: '/api/apm/settings/agent-configuration/new', - params: { - body: agentPayloadRt - }, +export const createOrUpdateAgentConfigurationRoute = createRoute(() => ({ + method: 'PUT', + path: '/api/apm/settings/agent-configuration', options: { tags: ['access:apm', 'access:apm_write'] }, + params: { + query: t.partial({ overwrite: jsonRt.pipe(t.boolean) }), + body: agentConfigurationIntakeRt + }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const configuration = context.params.body; + const { body, query } = context.params; - // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764) - context.logger.info( - `Hitting: /api/apm/settings/agent-configuration/new with ${configuration.service.name}/${configuration.service.environment}` - ); - const res = await createOrUpdateConfiguration({ - configuration, + // if the config already exists, it is fetched and updated + // this is to avoid creating two configs with identical service params + const config = await findExactConfiguration({ + service: body.service, setup }); - context.logger.info(`Created agent configuration`); - return res; - } -})); + // if the config exists ?overwrite=true is required + if (config && !query.overwrite) { + throw Boom.badRequest( + `A configuration already exists for "${body.service.name}/${body.service.environment}. Use ?overwrite=true to overwrite the existing configuration.` + ); + } + + context.logger.info( + `${config ? 'Updating' : 'Creating'} config ${body.service.name}/${ + body.service.environment + }` + ); -export const updateAgentConfigurationRoute = createRoute(() => ({ - method: 'PUT', - path: '/api/apm/settings/agent-configuration/{configurationId}', - options: { - tags: ['access:apm', 'access:apm_write'] - }, - params: { - path: t.type({ - configurationId: t.string - }), - body: agentPayloadRt - }, - handler: async ({ context, request }) => { - const setup = await setupRequest(context, request); - const { configurationId } = context.params.path; return await createOrUpdateConfiguration({ - configurationId, - configuration: context.params.body, + configurationId: config?._id, + configurationIntake: body, setup }); } @@ -162,41 +155,33 @@ export const agentConfigurationSearchRoute = createRoute(core => ({ path: '/api/apm/settings/agent-configuration/search', params: { body: t.type({ - service: t.intersection([ - t.type({ name: t.string }), - t.partial({ environment: t.string }) - ]), + service: serviceRt, etag: t.string }) }, handler: async ({ context, request }) => { - const { body } = context.params; - - // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764) - context.logger.info( - `Hitting: /api/apm/settings/agent-configuration/search for ${body.service.name}/${body.service.environment}` - ); + const { service, etag } = context.params.body; const setup = await setupRequest(context, request); const config = await searchConfigurations({ - serviceName: body.service.name, - environment: body.service.environment, + service, setup }); if (!config) { context.logger.info( - `Config was not found for ${body.service.name}/${body.service.environment}` + `Config was not found for ${service.name}/${service.environment}` ); - throw new Boom('Not found', { statusCode: 404 }); + throw Boom.notFound(); } context.logger.info( - `Config was found for ${body.service.name}/${body.service.environment}` + `Config was found for ${service.name}/${service.environment}` ); // update `applied_by_agent` field if etags match - if (body.etag === config._source.etag && !config._source.applied_by_agent) { + // this happens in the background and doesn't block the response + if (etag === config._source.etag && !config._source.applied_by_agent) { markAppliedByAgent({ id: config._id, body: config._source, setup }); } diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts index 12e08869fa5861..959a0c97acfa3d 100644 --- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts +++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { AgentConfigurationIntake } from '../../../../plugins/apm/server/lib/settings/agent_configuration/configuration_types'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function agentConfigurationTests({ getService }: FtrProviderContext) { @@ -18,108 +19,122 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo'); } - let createdConfigIds: any[] = []; - async function createConfiguration(configuration: any) { + async function createConfiguration(config: AgentConfigurationIntake) { + log.debug('creating configuration', config.service); const res = await supertest - .post(`/api/apm/settings/agent-configuration/new`) - .send(configuration) + .put(`/api/apm/settings/agent-configuration`) + .send(config) .set('kbn-xsrf', 'foo'); - createdConfigIds.push(res.body._id); + throwOnError(res); return res; } - function deleteCreatedConfigurations() { - const promises = Promise.all(createdConfigIds.map(deleteConfiguration)); - return promises; + async function updateConfiguration(config: AgentConfigurationIntake) { + log.debug('updating configuration', config.service); + const res = await supertest + .put(`/api/apm/settings/agent-configuration?overwrite=true`) + .send(config) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; } - function deleteConfiguration(configurationId: string) { - return supertest - .delete(`/api/apm/settings/agent-configuration/${configurationId}`) - .set('kbn-xsrf', 'foo') - .then((response: any) => { - createdConfigIds = createdConfigIds.filter(id => id === configurationId); - return response; - }); + async function deleteConfiguration({ service }: AgentConfigurationIntake) { + log.debug('deleting configuration', service); + const res = await supertest + .delete(`/api/apm/settings/agent-configuration`) + .send({ service }) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + function throwOnError(res: any) { + const { statusCode, req, body } = res; + if (statusCode !== 200) { + throw new Error(` + Endpoint: ${req.method} ${req.path} + Service: ${JSON.stringify(res.request._data.service)} + Status code: ${statusCode} + Response: ${body.message}`); + } } describe('agent configuration', () => { describe('when creating one configuration', () => { - let createdConfigId: string; + const newConfig = { + service: {}, + settings: { transaction_sample_rate: 0.55 }, + }; - const parameters = { + const searchParams = { service: { name: 'myservice', environment: 'development' }, etag: '7312bdcc34999629a3d39df24ed9b2a7553c0c39', }; before(async () => { - log.debug('creating agent configuration'); - - // all / all - const { body } = await createConfiguration({ - service: {}, - settings: { transaction_sample_rate: 0.1 }, - }); - - createdConfigId = body._id; + await createConfiguration(newConfig); }); - it('returns the created configuration', async () => { - const { statusCode, body } = await searchConfigurations(parameters); - + it('can find the created config', async () => { + const { statusCode, body } = await searchConfigurations(searchParams); expect(statusCode).to.equal(200); - expect(body._id).to.equal(createdConfigId); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: 0.55 }); }); - it('succesfully deletes the configuration', async () => { - await deleteConfiguration(createdConfigId); + it('can update the created config', async () => { + await updateConfiguration({ service: {}, settings: { transaction_sample_rate: 0.85 } }); - const { statusCode } = await searchConfigurations(parameters); + const { statusCode, body } = await searchConfigurations(searchParams); + expect(statusCode).to.equal(200); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: 0.85 }); + }); + it('can delete the created config', async () => { + await deleteConfiguration(newConfig); + const { statusCode } = await searchConfigurations(searchParams); expect(statusCode).to.equal(404); }); }); - describe('when creating four configurations', () => { - before(async () => { - log.debug('creating agent configuration'); - - // all / all - await createConfiguration({ + describe('when creating multiple configurations', () => { + const configs = [ + { service: {}, settings: { transaction_sample_rate: 0.1 }, - }); - - // my_service / all - await createConfiguration({ + }, + { service: { name: 'my_service' }, settings: { transaction_sample_rate: 0.2 }, - }); - - // all / production - await createConfiguration({ - service: { environment: 'production' }, + }, + { + service: { name: 'my_service', environment: 'development' }, settings: { transaction_sample_rate: 0.3 }, - }); - - // all / production - await createConfiguration({ - service: { environment: 'development' }, + }, + { + service: { environment: 'production' }, settings: { transaction_sample_rate: 0.4 }, - }); - - // my_service / production - await createConfiguration({ - service: { name: 'my_service', environment: 'development' }, + }, + { + service: { environment: 'development' }, settings: { transaction_sample_rate: 0.5 }, - }); + }, + ]; + + before(async () => { + await Promise.all(configs.map(config => createConfiguration(config))); }); after(async () => { - log.debug('deleting agent configurations'); - await deleteCreatedConfigurations(); + await Promise.all(configs.map(config => deleteConfiguration(config))); }); const agentsRequests = [ @@ -127,20 +142,24 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte service: { name: 'non_existing_service', environment: 'non_existing_env' }, expectedSettings: { transaction_sample_rate: 0.1 }, }, + { + service: { name: 'my_service', environment: 'non_existing_env' }, + expectedSettings: { transaction_sample_rate: 0.2 }, + }, { service: { name: 'my_service', environment: 'production' }, expectedSettings: { transaction_sample_rate: 0.2 }, }, { - service: { name: 'non_existing_service', environment: 'production' }, + service: { name: 'my_service', environment: 'development' }, expectedSettings: { transaction_sample_rate: 0.3 }, }, { - service: { name: 'non_existing_service', environment: 'development' }, + service: { name: 'non_existing_service', environment: 'production' }, expectedSettings: { transaction_sample_rate: 0.4 }, }, { - service: { name: 'my_service', environment: 'development' }, + service: { name: 'non_existing_service', environment: 'development' }, expectedSettings: { transaction_sample_rate: 0.5 }, }, ]; @@ -159,18 +178,18 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); describe('when an agent retrieves a configuration', () => { + const config = { + service: { name: 'myservice', environment: 'development' }, + settings: { transaction_sample_rate: 0.9 }, + }; + before(async () => { log.debug('creating agent configuration'); - - await createConfiguration({ - service: { name: 'myservice', environment: 'development' }, - settings: { transaction_sample_rate: 0.9 }, - }); + await createConfiguration(config); }); after(async () => { - log.debug('deleting agent configurations'); - await deleteCreatedConfigurations(); + await deleteConfiguration(config); }); it(`should have 'applied_by_agent=false' on first request`, async () => { diff --git a/x-pack/test/api_integration/apis/apm/feature_controls.ts b/x-pack/test/api_integration/apis/apm/feature_controls.ts index ec2bbca23ddd28..3c5314d0d32613 100644 --- a/x-pack/test/api_integration/apis/apm/feature_controls.ts +++ b/x-pack/test/api_integration/apis/apm/feature_controls.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -/* eslint-disable no-console */ - import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -14,8 +12,8 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const supertestWithoutAuth = getService('supertestWithoutAuth'); const security = getService('security'); const spaces = getService('spaces'); - const log = getService('log'); const es = getService('legacyEs'); + const log = getService('log'); const start = encodeURIComponent(new Date(Date.now() - 10000).toISOString()); const end = encodeURIComponent(new Date().toISOString()); @@ -33,7 +31,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) interface Endpoint { req: { url: string; - method?: 'get' | 'post' | 'delete'; + method?: 'get' | 'post' | 'delete' | 'put'; body?: any; }; expectForbidden: (result: any) => void; @@ -148,7 +146,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) index: '.apm-agent-configuration', }); - console.warn(JSON.stringify(res, null, 2)); + log.error(JSON.stringify(res, null, 2)); }, }, ]; @@ -196,7 +194,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const { statusCode, req } = response; if (statusCode !== 200) { - log.debug(`Endpoint: ${req.method} ${req.path} + throw new Error(`Endpoint: ${req.method} ${req.path} Status code: ${statusCode} Response: ${response.body.message}`); } @@ -216,9 +214,9 @@ export default function featureControlsTests({ getService }: FtrProviderContext) spaceId?: string; }) { for (const endpoint of endpoints) { - console.log(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`); + log.info(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`); const result = await executeAsUser(endpoint.req, username, password, spaceId); - console.log(`Responded: ${endpoint.req.url}`); + log.info(`Responded: ${endpoint.req.url}`); try { if (expectation === 'forbidden') { @@ -244,26 +242,28 @@ export default function featureControlsTests({ getService }: FtrProviderContext) } describe('apm feature controls', () => { - let res: any; + const config = { + service: { name: 'test-service' }, + settings: { transaction_sample_rate: 0.5 }, + }; before(async () => { - console.log(`Creating agent configuration`); - res = await executeAsAdmin({ - method: 'post', - url: '/api/apm/settings/agent-configuration/new', - body: { - service: { name: 'test-service' }, - settings: { transaction_sample_rate: 0.5 }, - }, + log.info(`Creating agent configuration`); + await executeAsAdmin({ + method: 'put', + url: '/api/apm/settings/agent-configuration', + body: config, }); - console.log(`Agent configuration created`); + log.info(`Agent configuration created`); }); after(async () => { - console.log('deleting agent configuration'); - const configurationId = res.body._id; + log.info('deleting agent configuration'); await executeAsAdmin({ method: 'delete', - url: `/api/apm/settings/agent-configuration/${configurationId}`, + url: `/api/apm/settings/agent-configuration`, + body: { + service: config.service, + }, }); }); diff --git a/x-pack/test/api_integration/apis/apm/index.ts b/x-pack/test/api_integration/apis/apm/index.ts index c49d5775370485..6f41f4abfecc3a 100644 --- a/x-pack/test/api_integration/apis/apm/index.ts +++ b/x-pack/test/api_integration/apis/apm/index.ts @@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('APM', () => { + describe('APM specs', () => { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./agent_configuration')); }); diff --git a/x-pack/test/functional/apps/apm/index.ts b/x-pack/test/functional/apps/apm/index.ts index 945af09183f03d..bf254f9b9b4193 100644 --- a/x-pack/test/functional/apps/apm/index.ts +++ b/x-pack/test/functional/apps/apm/index.ts @@ -6,7 +6,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { - describe('APM', function() { + describe('APM specs', function() { this.tags('ciGroup6'); loadTestFile(require.resolve('./feature_controls')); }); From 5eefdbb84266e2af1998c35d4876f18bc13d1fe0 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Mon, 24 Feb 2020 16:46:58 -0600 Subject: [PATCH 16/21] [Uptime] Use scripted metric for snapshot calculation (#58247) (#58389) Fixes #58079 This is an improved version of #58078 Note, this is a bugfix targeting 7.6.1 . I've decided to open this PR directly against 7.6 in the interest of time. We can forward-port this to 7.x / master later. This patch improves the handling of timespans with snapshot counts. This feature originally worked, but suffered a regression when we increased the default timespan in the query context to 5m. This means that without this patch the counts you get are the maximum total number of monitors that were down over the past 5m, which is not really that useful. We now use a scripted metric to always count precisely the number of up/down monitors. On my box this could process 400k summary docs in ~600ms. This should scale as shards are added. I attempted to keep memory usage relatively slow by using simple maps of strings. --- .../lib/requests/get_snapshot_counts.ts | 194 +++++++++++++----- .../apis/uptime/rest/snapshot.ts | 105 +++++----- 2 files changed, 192 insertions(+), 107 deletions(-) diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts index 62369711460159..8d84c0f4d6769f 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts @@ -6,7 +6,7 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { Snapshot } from '../../../common/runtime_types'; -import { QueryContext, MonitorGroupIterator } from './search'; +import { QueryContext } from './search'; import { CONTEXT_DEFAULTS, INDEX_NAMES } from '../../../common/constants'; export interface GetSnapshotCountParams { @@ -16,49 +16,6 @@ export interface GetSnapshotCountParams { statusFilter?: string; } -const fastStatusCount = async (context: QueryContext): Promise => { - const params = { - index: INDEX_NAMES.HEARTBEAT, - body: { - size: 0, - query: { bool: { filter: await context.dateAndCustomFilters() } }, - aggs: { - unique: { - // We set the precision threshold to 40k which is the max precision supported by cardinality - cardinality: { field: 'monitor.id', precision_threshold: 40000 }, - }, - down: { - filter: { range: { 'summary.down': { gt: 0 } } }, - aggs: { - unique: { cardinality: { field: 'monitor.id', precision_threshold: 40000 } }, - }, - }, - }, - }, - }; - - const statistics = await context.search(params); - const total = statistics.aggregations.unique.value; - const down = statistics.aggregations.down.unique.value; - - return { - total, - down, - up: total - down, - }; -}; - -const slowStatusCount = async (context: QueryContext, status: string): Promise => { - const downContext = context.clone(); - downContext.statusFilter = status; - const iterator = new MonitorGroupIterator(downContext); - let count = 0; - while (await iterator.next()) { - count++; - } - return count; -}; - export const getSnapshotCount: UMElasticsearchQueryFn = async ({ callES, dateRangeStart, @@ -81,18 +38,7 @@ export const getSnapshotCount: UMElasticsearchQueryFn = - counts.up > counts.down ? ['down', 'up'] : ['up', 'down']; - counts[leastCommonStatus] = await slowStatusCount(context, leastCommonStatus); - counts[mostCommonStatus] = counts.total - counts[leastCommonStatus]; - } + const counts = await statusCount(context); return { total: statusFilter ? counts[statusFilter] : counts.total, @@ -100,3 +46,139 @@ export const getSnapshotCount: UMElasticsearchQueryFn => { + const res = await context.search({ + index: INDEX_NAMES.HEARTBEAT, + body: statusCountBody(await context.dateAndCustomFilters()), + }); + + return res.aggregations.counts.value; +}; + +const statusCountBody = (filters: any): any => { + return { + size: 0, + query: { + bool: { + filter: [ + { + exists: { + field: 'summary', + }, + }, + filters, + ], + }, + }, + aggs: { + counts: { + scripted_metric: { + init_script: 'state.locStatus = new HashMap(); state.totalDocs = 0;', + map_script: ` + def loc = doc["observer.geo.name"].size() == 0 ? "" : doc["observer.geo.name"][0]; + + // One concern here is memory since we could build pretty gigantic maps. I've opted to + // stick to a simple map to reduce memory overhead. This means we do + // a little string parsing to treat these strings as records that stay lexicographically + // sortable (which is important later). + // We encode the ID and location as $id.len:$id$loc + String id = doc["monitor.id"][0]; + String idLenDelim = Integer.toHexString(id.length()) + ":" + id; + String idLoc = loc == null ? idLenDelim : idLenDelim + loc; + + String status = doc["summary.down"][0] > 0 ? "d" : "u"; + String timeAndStatus = doc["@timestamp"][0].toInstant().toEpochMilli().toString() + status; + state.locStatus[idLoc] = timeAndStatus; + state.totalDocs++; + `, + combine_script: ` + return state; + `, + reduce_script: ` + // Use a treemap since it's traversable in sorted order. + // This is important later. + TreeMap locStatus = new TreeMap(); + long totalDocs = 0; + int uniqueIds = 0; + for (state in states) { + totalDocs += state.totalDocs; + for (entry in state.locStatus.entrySet()) { + // Update the value for the given key if we have a more recent check from this location. + locStatus.merge(entry.getKey(), entry.getValue(), (a,b) -> a.compareTo(b) > 0 ? a : b) + } + } + + HashMap locTotals = new HashMap(); + int total = 0; + int down = 0; + String curId = ""; + boolean curIdDown = false; + // We now iterate through our tree map in order, which means records for a given ID + // always are encountered one after the other. This saves us having to make an intermediate + // map. + for (entry in locStatus.entrySet()) { + String idLoc = entry.getKey(); + String timeStatus = entry.getValue(); + + // Parse the length delimited id/location strings described in the map section + int colonIndex = idLoc.indexOf(":"); + int idEnd = Integer.parseInt(idLoc.substring(0, colonIndex), 16) + colonIndex + 1; + String id = idLoc.substring(colonIndex + 1, idEnd); + String loc = idLoc.substring(idEnd, idLoc.length()); + String status = timeStatus.substring(timeStatus.length() - 1); + + // Here we increment counters for the up/down key per location + // We also create a new hashmap in locTotals if we've never seen this location + // before. + locTotals.compute(loc, (k,v) -> { + HashMap res = v; + if (v == null) { + res = new HashMap(); + res.put('up', 0); + res.put('down', 0); + } + + if (status == 'u') { + res.up++; + } else { + res.down++; + } + + return res; + }); + + + // We've encountered a new ID + if (curId != id) { + total++; + curId = id; + if (status == "d") { + curIdDown = true; + down++; + } else { + curIdDown = false; + } + } else if (!curIdDown) { + if (status == "d") { + curIdDown = true; + down++; + } else { + curIdDown = false; + } + } + } + + Map result = new HashMap(); + result.total = total; + result.location_totals = locTotals; + result.up = total - down; + result.down = down; + result.totalDocs = totalDocs; + return result; + `, + }, + }, + }, + }; +}; diff --git a/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts b/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts index b0d97837c770f9..20fe59d149ae8f 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts @@ -34,66 +34,69 @@ export default function({ getService }: FtrProviderContext) { let dateRange: { start: string; end: string }; [true, false].forEach(async (includeTimespan: boolean) => { - describe(`with timespans ${includeTimespan ? 'included' : 'missing'}`, async () => { - before(async () => { - const promises: Array> = []; - - // When includeTimespan is false we have to remove the values there. - let mogrify = (d: any) => d; - if ((includeTimespan = false)) { - mogrify = (d: any): any => { - d.monitor.delete('timespan'); + [true, false].forEach(async (includeObserver: boolean) => { + describe(`with timespans=${includeTimespan} and observer=${includeObserver}`, async () => { + before(async () => { + const promises: Array> = []; + + const mogrify = (d: any) => { + if (!includeTimespan) { + delete d.monitor.timespan; + } + if (!includeObserver) { + delete d.observer; + } return d; }; - } - - const makeMonitorChecks = async (monitorId: string, status: 'up' | 'down') => { - return makeChecksWithStatus( - getService('legacyEs'), - monitorId, - checksPerMonitor, - numIps, - scheduleEvery, - {}, - status, - mogrify - ); - }; - for (let i = 0; i < numUpMonitors; i++) { - promises.push(makeMonitorChecks(`up-${i}`, 'up')); - } - for (let i = 0; i < numDownMonitors; i++) { - promises.push(makeMonitorChecks(`down-${i}`, 'down')); - } + const makeMonitorChecks = async (monitorId: string, status: 'up' | 'down') => { + return makeChecksWithStatus( + getService('legacyEs'), + monitorId, + checksPerMonitor, + numIps, + scheduleEvery, + {}, + status, + mogrify + ); + }; - const allResults = await Promise.all(promises); - dateRange = getChecksDateRange(allResults); - }); + for (let i = 0; i < numUpMonitors; i++) { + promises.push(makeMonitorChecks(`up-${i}`, 'up')); + } + for (let i = 0; i < numDownMonitors; i++) { + promises.push(makeMonitorChecks(`down-${i}`, 'down')); + } - it('will count all statuses correctly', async () => { - const apiResponse = await supertest.get( - `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}` - ); + const allResults = await Promise.all(promises); + dateRange = getChecksDateRange(allResults); + }); - expectFixtureEql(apiResponse.body, 'snapshot'); - }); + it('will count all statuses correctly', async () => { + const apiResponse = await supertest.get( + `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}` + ); - it('will fetch a monitor snapshot filtered by down status', async () => { - const statusFilter = 'down'; - const apiResponse = await supertest.get( - `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` - ); + expectFixtureEql(apiResponse.body, 'snapshot'); + }); - expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_down'); - }); + it('will fetch a monitor snapshot filtered by down status', async () => { + const statusFilter = 'down'; + const apiResponse = await supertest.get( + `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` + ); - it('will fetch a monitor snapshot filtered by up status', async () => { - const statusFilter = 'up'; - const apiResponse = await supertest.get( - `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` - ); - expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_up'); + expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_down'); + }); + + it('will fetch a monitor snapshot filtered by up status', async () => { + const statusFilter = 'up'; + const apiResponse = await supertest.get( + `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` + ); + expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_up'); + }); }); }); }); From a00ebc6f187ffbd920b0e2bf3f92483239ed0c0f Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 24 Feb 2020 18:44:52 -0500 Subject: [PATCH 17/21] [Uptime] Delete useless try...catch blocks (#58263) * Delete useless try...catch blocks. * Delete unneeded function. Co-authored-by: Elastic Machine --- .../plugins/uptime/public/hooks/use_telemetry.ts | 10 +++------- .../plugins/uptime/server/lib/helper/index.ts | 1 - .../uptime/server/lib/helper/parse_filter_query.ts | 13 ------------- .../server/lib/requests/get_ping_histogram.ts | 4 ++-- 4 files changed, 5 insertions(+), 23 deletions(-) delete mode 100644 x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts index 15f276174e2cf1..7eb18404decfde 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts @@ -22,13 +22,9 @@ const getApiPath = (page?: UptimePage) => { }; const logPageLoad = async (fetch: HttpHandler, page?: UptimePage) => { - try { - await fetch(getApiPath(page), { - method: 'POST', - }); - } catch (e) { - throw e; - } + await fetch(getApiPath(page), { + method: 'POST', + }); }; export const useUptimeTelemetry = (page?: UptimePage) => { diff --git a/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts b/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts index 0dcbc0a424b5eb..1607c36f1d1b78 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts @@ -7,6 +7,5 @@ export { getFilterClause } from './get_filter_clause'; export { parseRelativeDate } from './get_histogram_interval'; export { getHistogramIntervalFormatted } from './get_histogram_interval_formatted'; -export { parseFilterQuery } from './parse_filter_query'; export { assertCloseTo } from './assert_close_to'; export { objectValuesToArrays } from './object_to_array'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts b/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts deleted file mode 100644 index 4c73ec53af9b9b..00000000000000 --- a/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts +++ /dev/null @@ -1,13 +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 const parseFilterQuery = (query?: string | null) => { - try { - return query ? JSON.parse(query) : null; - } catch { - return null; - } -}; diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts index 3b448dc31659b7..7b8ca4708255c8 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts @@ -5,7 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { parseFilterQuery, getFilterClause } from '../helper'; +import { getFilterClause } from '../helper'; import { INDEX_NAMES, QUERY } from '../../../common/constants'; import { HistogramQueryResult } from './types'; import { HistogramResult } from '../../../common/types'; @@ -27,7 +27,7 @@ export const getPingHistogram: UMElasticsearchQueryFn< GetPingHistogramParams, HistogramResult > = async ({ callES, from, to, filters, monitorId, statusFilter }) => { - const boolFilters = parseFilterQuery(filters); + const boolFilters = filters ? JSON.parse(filters) : null; const additionalFilters = []; if (monitorId) { additionalFilters.push({ match: { 'monitor.id': monitorId } }); From 33334132eaacde693caa3f7b6a337309abd784bb Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 24 Feb 2020 17:07:36 -0700 Subject: [PATCH 18/21] [kbn/optimizer] disable parallelization in terser plugin (#58396) * [kbn/optimizer] disable parallelization in terer plugin * use more workers when building the dist --- .../kbn-optimizer/src/optimizer/optimizer_config.ts | 11 ++++++++++- packages/kbn-optimizer/src/worker/webpack.config.ts | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index a258e1010fce38..1c8ae265bf6bb8 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -25,6 +25,15 @@ import { Bundle, WorkerConfig } from '../common'; import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; import { getBundles } from './get_bundles'; +function pickMaxWorkerCount(dist: boolean) { + // don't break if cpus() returns nothing, or an empty array + const cpuCount = Math.max(Os.cpus()?.length, 1); + // if we're buiding the dist then we can use more of the system's resources to get things done a little quicker + const maxWorkers = dist ? cpuCount - 1 : Math.ceil(cpuCount / 3); + // ensure we always have at least two workers + return Math.max(maxWorkers, 2); +} + interface Options { /** absolute path to root of the repo/build */ repoRoot: string; @@ -110,7 +119,7 @@ export class OptimizerConfig { const maxWorkerCount = process.env.KBN_OPTIMIZER_MAX_WORKERS ? parseInt(process.env.KBN_OPTIMIZER_MAX_WORKERS, 10) - : options.maxWorkerCount ?? Math.max(Math.ceil(Math.max(Os.cpus()?.length, 1) / 3), 2); + : options.maxWorkerCount ?? pickMaxWorkerCount(dist); if (typeof maxWorkerCount !== 'number' || !Number.isFinite(maxWorkerCount)) { throw new TypeError('worker count must be a number'); } diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 22b927d4638d75..3c6ae78bc4d911 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -252,6 +252,7 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { cache: false, sourceMap: false, extractComments: false, + parallel: false, terserOptions: { compress: false, mangle: false, From 277b38079ee55a5ae088a3a4c6bacf127f5c8968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Tue, 25 Feb 2020 13:12:27 +0530 Subject: [PATCH 19/21] [Index management] Move to new platform "plugins" folder (#58109) --- x-pack/.i18nrc.json | 2 +- .../cross_cluster_replication/index.js | 7 +- .../components/detail_panel/detail_panel.js | 2 +- .../components/detail_panel/detail_panel.js | 7 +- .../public/extend_index_management/index.js | 9 ++- .../__jest__/extend_index_management.test.js | 6 +- .../index_lifecycle_management/plugin.ts | 8 +- .../public/legacy.ts | 4 +- .../components/policy_table/policy_table.js | 2 +- .../np_ready/extend_index_management/index.js | 3 +- .../legacy/plugins/index_management/index.ts | 29 +------ .../index_management/public/index.html | 3 - .../legacy/plugins/remote_clusters/index.ts | 4 +- x-pack/legacy/plugins/rollup/index.ts | 4 +- x-pack/legacy/plugins/rollup/public/legacy.ts | 2 - x-pack/legacy/plugins/rollup/public/plugin.ts | 20 +++-- x-pack/legacy/plugins/rollup/server/plugin.ts | 10 +-- .../rollup/server/rollup_data_enricher.ts | 7 +- x-pack/legacy/plugins/rollup/server/types.ts | 1 - .../client_integration/helpers/constants.ts | 0 .../helpers/home.helpers.ts | 6 +- .../helpers/http_requests.ts | 0 .../client_integration/helpers/index.ts | 2 +- .../helpers/setup_environment.tsx | 3 +- .../helpers/template_clone.helpers.ts | 4 +- .../helpers/template_create.helpers.ts | 4 +- .../helpers/template_edit.helpers.ts | 4 +- .../helpers/template_form.helpers.ts | 2 +- .../__jest__/client_integration/home.test.ts | 0 .../template_clone.test.tsx | 0 .../template_create.test.tsx | 0 .../client_integration/template_edit.test.tsx | 0 .../__snapshots__/index_table.test.js.snap | 0 .../__jest__/components/index_table.test.js | 4 +- .../__snapshots__/flatten_object.test.js.snap | 0 .../__jest__/lib/flatten_object.test.js | 0 .../plugins/index_management/__mocks__/ace.js | 0 .../__mocks__/ui/documentation_links.js | 0 .../index_management/__mocks__/ui/notify.js | 0 .../common/constants/api_base_path.ts | 0 .../common/constants/base_path.ts | 0 .../common/constants/index.ts | 0 .../common/constants/index_statuses.ts | 0 .../common/constants/invalid_characters.ts | 0 .../common/constants/plugin.ts | 2 +- .../common/constants/ui_metric.ts | 0 .../plugins/index_management/common/index.ts | 0 .../index_management/common/lib/index.ts | 0 .../common/lib/template_serialization.ts | 0 .../index_management/common/types/index.ts | 0 .../common/types/templates.ts | 0 x-pack/plugins/index_management/kibana.json | 15 ++++ .../public/application/app.tsx | 2 +- .../public/application/app_context.tsx | 4 +- .../public/application/components/index.ts | 0 .../client_integration/helpers/index.ts | 2 +- .../helpers/mappings_editor.helpers.ts | 2 +- .../mappings_editor.test.tsx | 0 .../components/mappings_editor/_index.scss | 0 .../mappings_editor/components/_index.scss | 0 .../mappings_editor/components/code_block.tsx | 0 .../configuration_form/configuration_form.tsx | 6 +- .../configuration_form_schema.tsx | 0 .../dynamic_mapping_section.tsx | 0 .../dynamic_mapping_section/index.ts | 0 .../components/configuration_form/index.ts | 0 .../meta_field_section/index.ts | 0 .../meta_field_section/meta_field_section.tsx | 0 .../configuration_form/routing_section.tsx | 0 .../source_field_section/index.ts | 0 .../source_field_section.tsx | 0 .../components/document_fields/_index.scss | 0 .../document_fields/document_fields.tsx | 11 ++- .../document_fields_header.tsx | 0 .../editor_toggle_controls.tsx | 0 .../field_parameters/analyzer_parameter.tsx | 0 .../analyzer_parameter_selects.tsx | 20 +++-- .../field_parameters/analyzers_parameter.tsx | 0 .../field_parameters/boost_parameter.tsx | 0 .../coerce_number_parameter.tsx | 0 .../coerce_shape_parameter.tsx | 0 .../field_parameters/copy_to_parameter.tsx | 0 .../field_parameters/doc_values_parameter.tsx | 0 .../field_parameters/dynamic_parameter.tsx | 0 .../eager_global_ordinals_parameter.tsx | 0 .../field_parameters/enabled_parameter.tsx | 0 .../fielddata_frequency_filter_absolute.tsx | 0 .../fielddata_frequency_filter_percentage.tsx | 0 .../field_parameters/fielddata_parameter.tsx | 0 .../field_parameters/format_parameter.tsx | 0 .../field_parameters/ignore_malformed.tsx | 0 .../ignore_z_value_parameter.tsx | 0 .../document_fields/field_parameters/index.ts | 0 .../field_parameters/index_parameter.tsx | 0 .../field_parameters/locale_parameter.tsx | 0 .../max_shingle_size_parameter.tsx | 0 .../field_parameters/name_parameter.tsx | 0 .../field_parameters/norms_parameter.tsx | 0 .../field_parameters/null_value_parameter.tsx | 0 .../orientation_parameter.tsx | 0 .../field_parameters/path_parameter.tsx | 0 .../field_parameters/relations_parameter.tsx | 0 .../field_parameters/similarity_parameter.tsx | 0 .../split_queries_on_whitespace_parameter.tsx | 0 .../field_parameters/store_parameter.tsx | 0 .../term_vector_parameter.tsx | 0 .../field_parameters/type_parameter.tsx | 0 .../fields/_field_list_item.scss | 0 .../document_fields/fields/_index.scss | 0 .../fields/create_field/create_field.tsx | 80 +++++++++++-------- .../fields/create_field/index.ts | 0 .../required_parameters_forms/alias_type.tsx | 0 .../dense_vector_type.tsx | 0 .../required_parameters_forms/index.ts | 0 .../scaled_float_type.tsx | 0 .../token_count_type.tsx | 0 .../fields/delete_field_provider.tsx | 0 .../edit_field/_edit_field_form_row.scss | 0 .../fields/edit_field/_index.scss | 0 .../advanced_parameters_section.tsx | 0 .../edit_field/basic_parameters_section.tsx | 0 .../fields/edit_field/edit_field.tsx | 0 .../edit_field/edit_field_container.tsx | 4 +- .../fields/edit_field/edit_field_form_row.tsx | 0 .../edit_field/edit_field_header_form.tsx | 0 .../edit_field/field_description_section.tsx | 0 .../fields/edit_field/index.ts | 0 .../edit_field/update_field_provider.tsx | 0 .../fields/field_types/alias_type.tsx | 0 .../fields/field_types/binary_type.tsx | 0 .../fields/field_types/boolean_type.tsx | 0 .../fields/field_types/completion_type.tsx | 0 .../fields/field_types/date_type.tsx | 0 .../fields/field_types/dense_vector_type.tsx | 0 .../fields/field_types/flattened_type.tsx | 0 .../fields/field_types/geo_point_type.tsx | 0 .../fields/field_types/geo_shape_type.tsx | 0 .../fields/field_types/index.ts | 0 .../fields/field_types/ip_type.tsx | 0 .../fields/field_types/join_type.tsx | 0 .../fields/field_types/keyword_type.tsx | 0 .../fields/field_types/nested_type.tsx | 0 .../fields/field_types/numeric_type.tsx | 0 .../fields/field_types/object_type.tsx | 0 .../fields/field_types/range_type.tsx | 0 .../fields/field_types/search_as_you_type.tsx | 0 .../fields/field_types/shape_type.tsx | 0 .../fields/field_types/text_type.tsx | 0 .../fields/field_types/token_count_type.tsx | 0 .../document_fields/fields/fields_list.tsx | 0 .../fields/fields_list_item.tsx | 0 .../fields/fields_list_item_container.tsx | 10 +-- .../document_fields/fields/index.ts | 0 .../modal_confirmation_delete_fields.tsx | 0 .../document_fields/fields_json_editor.tsx | 0 .../document_fields/fields_tree_editor.tsx | 12 +-- .../components/document_fields/index.ts | 0 .../document_fields/search_fields/index.ts | 0 .../search_fields/search_result.tsx | 0 .../search_fields/search_result_item.tsx | 0 .../components/fields_tree.tsx | 0 .../mappings_editor/components/index.ts | 0 .../components/load_mappings/index.ts | 0 .../load_mappings/load_from_json_button.tsx | 0 .../load_mappings_provider.test.tsx | 2 +- .../load_mappings/load_mappings_provider.tsx | 0 .../components/multiple_mappings_warning.tsx | 0 .../components/templates_form/index.ts | 0 .../templates_form/templates_form.tsx | 6 +- .../templates_form/templates_form_schema.ts | 0 .../mappings_editor/components/tree/index.ts | 0 .../mappings_editor/components/tree/tree.tsx | 0 .../components/tree/tree_item.tsx | 0 .../constants/data_types_definition.tsx | 0 .../constants/default_values.ts | 0 .../constants/field_options.tsx | 0 .../constants/field_options_i18n.ts | 0 .../mappings_editor/constants/index.ts | 0 .../constants/mappings_editor.ts | 0 .../constants/parameters_definition.tsx | 0 .../components/mappings_editor/index.ts | 0 .../index_settings_context.tsx | 0 .../mappings_editor/lib/error_reporter.ts | 0 .../lib/extract_mappings_definition.test.ts | 0 .../lib/extract_mappings_definition.ts | 0 .../components/mappings_editor/lib/index.ts | 0 .../lib/mappings_validator.test.ts | 0 .../mappings_editor/lib/mappings_validator.ts | 0 .../mappings_editor/lib/search_fields.test.ts | 0 .../mappings_editor/lib/search_fields.tsx | 0 .../mappings_editor/lib/serializers.ts | 0 .../mappings_editor/lib/utils.test.ts | 0 .../components/mappings_editor/lib/utils.ts | 0 .../mappings_editor/lib/validators.ts | 0 .../mappings_editor/mappings_editor.tsx | 2 +- .../mappings_editor/mappings_state.tsx | 4 +- .../components/mappings_editor/reducer.ts | 0 .../mappings_editor/shared_imports.ts | 8 +- .../components/mappings_editor/types.ts | 0 .../application/components/no_match/index.ts | 0 .../components/no_match/no_match.tsx | 0 .../components/page_error/index.ts | 0 .../page_error/page_error_forbidden.tsx | 0 .../application/components/section_error.tsx | 0 .../components/section_loading.tsx | 0 .../components/template_delete_modal.tsx | 0 .../components/template_form/index.ts | 0 .../components/template_form/steps/index.ts | 0 .../template_form/steps/step_aliases.tsx | 0 .../template_form/steps/step_logistics.tsx | 15 +--- .../template_form/steps/step_mappings.tsx | 0 .../template_form/steps/step_review.tsx | 2 +- .../template_form/steps/step_settings.tsx | 0 .../template_form/steps/use_json_step.ts | 14 ++-- .../template_form/template_form.tsx | 34 ++++---- .../template_form/template_form_schemas.tsx | 5 +- .../template_form/template_steps.tsx | 0 .../components/template_form/types.ts | 0 .../constants/detail_panel_tabs.ts | 0 .../public/application/constants/index.ts | 0 .../constants/refresh_intervals.ts | 0 .../public/application/index.tsx | 2 +- .../public/application/lib/ace.js | 6 +- .../public/application/lib/edit_settings.js | 0 .../public/application/lib/flatten_object.js | 0 .../application/lib/flatten_panel_tree.js | 0 .../application/lib/index_status_labels.js | 0 .../lib/manage_angular_lifecycle.ts | 0 .../public/application/lib/render_badges.js | 0 .../public/application/sections/home/home.tsx | 0 .../public/application/sections/home/index.ts | 0 .../detail_panel/detail_panel.container.js | 0 .../index_list/detail_panel/detail_panel.js | 0 .../edit_settings_json.container.js | 0 .../edit_settings_json/edit_settings_json.js | 0 .../detail_panel/edit_settings_json/index.js | 0 .../home/index_list/detail_panel/index.js | 0 .../detail_panel/show_json/index.js | 0 .../show_json/show_json.container.js | 0 .../detail_panel/show_json/show_json.js | 0 .../index_list/detail_panel/summary/index.js | 0 .../detail_panel/summary/summary.container.js | 0 .../detail_panel/summary/summary.js | 0 .../sections/home/index_list/index.ts | 0 .../index_actions_context_menu/index.js | 0 .../index_actions_context_menu.container.js | 0 .../index_actions_context_menu.js | 0 .../sections/home/index_list/index_list.d.ts | 0 .../sections/home/index_list/index_list.js | 0 .../home/index_list/index_table/index.js | 0 .../index_table/index_table.container.js | 0 .../index_list/index_table/index_table.js | 0 .../sections/home/template_list/index.ts | 0 .../template_list/template_details/index.ts | 0 .../template_details/tabs/index.ts | 0 .../template_details/tabs/tab_aliases.tsx | 0 .../template_details/tabs/tab_mappings.tsx | 0 .../template_details/tabs/tab_settings.tsx | 0 .../template_details/tabs/tab_summary.tsx | 0 .../template_details/template_details.tsx | 4 +- .../home/template_list/template_list.tsx | 6 +- .../template_list/template_table/index.ts | 0 .../template_table/template_table.tsx | 0 .../sections/template_clone/index.ts | 0 .../template_clone/template_clone.tsx | 4 +- .../sections/template_create/index.ts | 0 .../template_create/template_create.tsx | 0 .../sections/template_edit/index.ts | 0 .../sections/template_edit/template_edit.tsx | 4 +- .../public/application/services/api.ts | 4 +- .../application/services/breadcrumbs.ts | 2 +- .../application/services/documentation.ts | 2 +- .../application/services/health_to_color.ts | 0 .../public/application/services/http.ts | 2 +- .../public/application/services/index.ts | 2 +- .../public/application/services/navigation.ts | 0 .../application/services/notification.ts | 2 +- .../public/application/services/routing.ts | 0 .../public/application/services/sort_table.ts | 0 .../public/application/services/ui_metric.ts | 6 +- .../application/services/use_request.ts | 0 .../store/actions/clear_cache_indices.js | 0 .../store/actions/clear_row_status.js | 0 .../store/actions/close_indices.js | 0 .../store/actions/delete_indices.js | 0 .../application/store/actions/detail_panel.js | 0 .../store/actions/edit_index_settings.js | 0 .../store/actions/extension_action.js | 0 .../store/actions/flush_indices.js | 0 .../store/actions/forcemerge_indices.js | 0 .../store/actions/freeze_indices.js | 0 .../public/application/store/actions/index.js | 0 .../store/actions/load_index_data.js | 0 .../application/store/actions/load_indices.js | 0 .../application/store/actions/open_indices.js | 0 .../store/actions/refresh_indices.js | 0 .../store/actions/reload_indices.js | 0 .../application/store/actions/table_state.js | 0 .../store/actions/unfreeze_indices.js | 0 .../store/actions/update_index_settings.js | 0 .../public/application/store/index.ts | 0 .../store/reducers/detail_panel.js | 0 .../application/store/reducers/index.js | 0 .../store/reducers/index_management.js | 0 .../application/store/reducers/indices.js | 0 .../application/store/reducers/row_status.js | 0 .../application/store/reducers/table_state.js | 0 .../application/store/selectors/index.d.ts | 0 .../application/store/selectors/index.js | 0 .../public/application/store/store.d.ts | 0 .../public/application/store/store.js | 0 .../index_management/public/index.scss | 0 .../plugins/index_management/public/index.ts | 11 +-- .../plugins/index_management/public/mocks.ts | 0 .../plugins/index_management/public/plugin.ts | 10 +-- .../services/extensions_service.mock.ts | 0 .../public/services/extensions_service.ts | 0 .../index_management/public/services/index.ts | 0 .../index_management/public/shared_imports.ts | 33 ++++++++ .../plugins/index_management/public/types.ts | 0 .../index_management/server/config.ts} | 14 ++-- .../plugins/index_management/server/index.ts | 10 +++ .../server/lib/fetch_aliases.test.ts | 0 .../server/lib/fetch_aliases.ts | 0 .../server/lib/fetch_indices.ts | 0 .../server/lib/get_managed_templates.ts | 0 .../server/lib/is_es_error.ts | 0 .../plugins/index_management/server/plugin.ts | 4 +- .../server/routes/api/index.ts | 0 .../server/routes/api/indices/index.ts | 0 .../api/indices/register_clear_cache_route.ts | 0 .../api/indices/register_close_route.ts | 0 .../api/indices/register_delete_route.ts | 0 .../api/indices/register_flush_route.ts | 0 .../api/indices/register_forcemerge_route.ts | 0 .../api/indices/register_freeze_route.ts | 0 .../api/indices/register_indices_routes.ts | 0 .../routes/api/indices/register_list_route.ts | 0 .../routes/api/indices/register_open_route.ts | 0 .../api/indices/register_refresh_route.ts | 0 .../api/indices/register_reload_route.ts | 0 .../api/indices/register_unfreeze_route.ts | 0 .../server/routes/api/mapping/index.ts | 0 .../api/mapping/register_mapping_route.ts | 0 .../server/routes/api/settings/index.ts | 0 .../api/settings/register_load_route.ts | 0 .../api/settings/register_settings_routes.ts | 0 .../api/settings/register_update_route.ts | 0 .../server/routes/api/stats/index.ts | 0 .../routes/api/stats/register_stats_route.ts | 0 .../server/routes/api/templates/index.ts | 0 .../api/templates/register_create_route.ts | 0 .../api/templates/register_delete_route.ts | 0 .../api/templates/register_get_routes.ts | 0 .../api/templates/register_template_routes.ts | 0 .../api/templates/register_update_route.ts | 0 .../routes/api/templates/validate_schemas.ts | 0 .../index_management/server/routes/helpers.ts | 0 .../index_management/server/routes/index.ts | 0 .../index_management/server/services/index.ts | 0 .../server/services/index_data_enricher.ts | 0 .../server/services/license.ts | 5 +- .../plugins/index_management/server/types.ts | 2 +- .../index_management/test/fixtures/index.ts | 0 .../test/fixtures/template.ts | 2 +- 365 files changed, 297 insertions(+), 261 deletions(-) delete mode 100644 x-pack/legacy/plugins/index_management/public/index.html rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/constants.ts (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts (96%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/index.ts (95%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx (95%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts (85%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts (84%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts (85%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts (99%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/home.test.ts (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/template_clone.test.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/template_create.test.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/template_edit.test.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/components/index_table.test.js (98%) rename x-pack/{legacy => }/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap (100%) rename x-pack/{legacy => }/plugins/index_management/__jest__/lib/flatten_object.test.js (100%) rename x-pack/{legacy => }/plugins/index_management/__mocks__/ace.js (100%) rename x-pack/{legacy => }/plugins/index_management/__mocks__/ui/documentation_links.js (100%) rename x-pack/{legacy => }/plugins/index_management/__mocks__/ui/notify.js (100%) rename x-pack/{legacy => }/plugins/index_management/common/constants/api_base_path.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/constants/base_path.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/constants/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/constants/index_statuses.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/constants/invalid_characters.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/constants/plugin.ts (86%) rename x-pack/{legacy => }/plugins/index_management/common/constants/ui_metric.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/lib/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/lib/template_serialization.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/types/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/common/types/templates.ts (100%) create mode 100644 x-pack/plugins/index_management/kibana.json rename x-pack/{legacy => }/plugins/index_management/public/application/app.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/app_context.tsx (90%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts (90%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts (85%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/_index.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/_index.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx (91%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx (86%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx (83%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx (97%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx (92%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx (88%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/reducer.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts (72%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/types.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/no_match/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/no_match/no_match.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/page_error/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/section_error.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/section_loading.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_delete_modal.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx (94%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_review.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts (83%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/template_form.tsx (92%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx (96%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/template_steps.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/types.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/constants/detail_panel_tabs.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/constants/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/constants/refresh_intervals.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/index.tsx (94%) rename x-pack/{legacy => }/plugins/index_management/public/application/lib/ace.js (94%) rename x-pack/{legacy => }/plugins/index_management/public/application/lib/edit_settings.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/lib/flatten_object.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/lib/flatten_panel_tree.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/lib/index_status_labels.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/lib/render_badges.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/home.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_list.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_table/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_list.tsx (97%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_clone/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_clone/template_clone.tsx (96%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_create/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_create/template_create.tsx (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_edit/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_edit/template_edit.tsx (96%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/api.ts (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/breadcrumbs.ts (96%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/documentation.ts (98%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/health_to_color.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/http.ts (88%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/index.ts (96%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/navigation.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/notification.ts (93%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/routing.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/sort_table.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/ui_metric.ts (90%) rename x-pack/{legacy => }/plugins/index_management/public/application/services/use_request.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/clear_cache_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/clear_row_status.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/close_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/delete_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/detail_panel.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/edit_index_settings.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/extension_action.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/flush_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/forcemerge_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/freeze_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/load_index_data.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/load_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/open_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/refresh_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/reload_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/table_state.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/unfreeze_indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/update_index_settings.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/detail_panel.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/index_management.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/indices.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/row_status.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/table_state.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/selectors/index.d.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/selectors/index.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/store.d.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/application/store/store.js (100%) rename x-pack/{legacy => }/plugins/index_management/public/index.scss (100%) rename x-pack/{legacy => }/plugins/index_management/public/index.ts (66%) rename x-pack/{legacy => }/plugins/index_management/public/mocks.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/plugin.ts (90%) rename x-pack/{legacy => }/plugins/index_management/public/services/extensions_service.mock.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/services/extensions_service.ts (100%) rename x-pack/{legacy => }/plugins/index_management/public/services/index.ts (100%) create mode 100644 x-pack/plugins/index_management/public/shared_imports.ts rename x-pack/{legacy => }/plugins/index_management/public/types.ts (100%) rename x-pack/{legacy/plugins/index_management/public/shared_imports.ts => plugins/index_management/server/config.ts} (52%) rename x-pack/{legacy => }/plugins/index_management/server/index.ts (67%) rename x-pack/{legacy => }/plugins/index_management/server/lib/fetch_aliases.test.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/lib/fetch_aliases.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/lib/fetch_indices.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/lib/get_managed_templates.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/lib/is_es_error.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/plugin.ts (94%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_close_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_delete_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_flush_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_freeze_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_indices_routes.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_list_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_open_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_refresh_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_reload_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/mapping/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/register_load_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/register_settings_routes.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/register_update_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/stats/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/stats/register_stats_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_create_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_delete_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_get_routes.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_template_routes.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_update_route.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/validate_schemas.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/helpers.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/routes/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/services/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/services/index_data_enricher.ts (100%) rename x-pack/{legacy => }/plugins/index_management/server/services/license.ts (89%) rename x-pack/{legacy => }/plugins/index_management/server/types.ts (92%) rename x-pack/{legacy => }/plugins/index_management/test/fixtures/index.ts (100%) rename x-pack/{legacy => }/plugins/index_management/test/fixtures/template.ts (88%) diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index f22f7e98d3b8a4..b1cb9075434e87 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -16,7 +16,7 @@ "xpack.fileUpload": "legacy/plugins/file_upload", "xpack.graph": ["legacy/plugins/graph", "plugins/graph"], "xpack.grokDebugger": "legacy/plugins/grokdebugger", - "xpack.idxMgmt": "legacy/plugins/index_management", + "xpack.idxMgmt": "plugins/index_management", "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management", "xpack.infra": "plugins/infra", "xpack.ingestManager": "plugins/ingest_manager", diff --git a/x-pack/legacy/plugins/cross_cluster_replication/index.js b/x-pack/legacy/plugins/cross_cluster_replication/index.js index 1b5f42fc5107eb..1b3aafcad5c0f6 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/index.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/index.js @@ -49,13 +49,12 @@ export function crossClusterReplication(kibana) { init: function initCcrPlugin(server) { registerLicenseChecker(server); registerRoutes(server); - if ( server.config().get('xpack.ccr.ui.enabled') && - server.plugins.index_management && - server.plugins.index_management.addIndexManagementDataEnricher + server.newPlatform.setup.plugins.indexManagement && + server.newPlatform.setup.plugins.indexManagement.indexDataEnricher ) { - server.plugins.index_management.addIndexManagementDataEnricher(ccrDataEnricher); + server.newPlatform.setup.plugins.indexManagement.indexDataEnricher.add(ccrDataEnricher); } }, }); diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js index 16ed0a7d695ad5..7b31ffa5024b76 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js @@ -7,7 +7,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation'; +import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public'; import { EuiButtonEmpty, diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js index e3bda2e67097d2..2ad118d28f38d7 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js @@ -7,8 +7,6 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation'; - import { EuiButton, EuiButtonEmpty, @@ -31,12 +29,11 @@ import { EuiTextColor, EuiTitle, } from '@elastic/eui'; - import 'brace/theme/textmate'; -import { ContextMenu } from '../context_menu'; - +import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public'; import { API_STATUS } from '../../../../../constants'; +import { ContextMenu } from '../context_menu'; export class DetailPanel extends Component { static propTypes = { diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js b/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js index 809a7c3e87b75e..c44918c500849b 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { extensionsService } from '../../../index_management/public'; +import { npSetup } from 'ui/new_platform'; import { get } from 'lodash'; const propertyPath = 'isFollowerIndex'; -export const followerBadgeExtension = { + +const followerBadgeExtension = { matchIndex: index => { return get(index, propertyPath); }, @@ -19,4 +20,6 @@ export const followerBadgeExtension = { filterExpression: 'isFollowerIndex:true', }; -extensionsService.addBadge(followerBadgeExtension); +if (npSetup.plugins.indexManagement) { + npSetup.plugins.indexManagement.extensionsService.addBadge(followerBadgeExtension); +} diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js index bcbae7e093f465..d2619778617c3e 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js @@ -27,8 +27,10 @@ initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path); initUiMetric(() => () => {}); jest.mock('ui/new_platform'); -jest.mock('../../index_management/public', async () => { - const { indexManagementMock } = await import('../../index_management/public/mocks.ts'); +jest.mock('../../../../plugins/index_management/public', async () => { + const { indexManagementMock } = await import( + '../../../../plugins/index_management/public/mocks.ts' + ); return indexManagementMock.createSetup(); }); diff --git a/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts b/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts index 8d7f937039203f..38d1bea45ce070 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts +++ b/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts @@ -41,14 +41,14 @@ export class Plugin { registerPoliciesRoutes(server); registerTemplatesRoutes(server); - const serverPlugins = server.plugins as any; + const serverPlugins = server.newPlatform.setup.plugins as any; if ( server.config().get('xpack.ilm.ui.enabled') && - serverPlugins.index_management && - serverPlugins.index_management.addIndexManagementDataEnricher + serverPlugins.indexManagement && + serverPlugins.indexManagement.indexDataEnricher ) { - serverPlugins.index_management.addIndexManagementDataEnricher(indexLifecycleDataEnricher); + serverPlugins.indexManagement.indexDataEnricher.add(indexLifecycleDataEnricher); } } } diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts b/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts index 3c21a644c0f783..f7f924add2c514 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts @@ -20,7 +20,9 @@ import { addAllExtensions } from './np_ready/extend_index_management'; if (chrome.getInjected('ilmUiEnabled')) { // We have to initialize this outside of the NP lifecycle, otherwise these extensions won't // be available in Index Management unless the user visits ILM first. - addAllExtensions(); + if ((npSetup.plugins as any).indexManagement) { + addAllExtensions((npSetup.plugins as any).indexManagement.extensionsService); + } // This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular // from destroying scope when route changes and both old route and new route are this same route. diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js index 83d32eae1097d7..903161fe094fc3 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js @@ -37,7 +37,7 @@ import { import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; -import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation'; +import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public'; import { BASE_PATH } from '../../../../../../../common/constants'; import { UIM_EDIT_CLICK } from '../../../../constants'; import { getPolicyPath } from '../../../../services/navigation'; diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js index 0e662b78b2c180..69658d31695bce 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js @@ -9,7 +9,6 @@ import { get, every, any } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiSearchBar } from '@elastic/eui'; -import { extensionsService } from '../../../../index_management/public'; import { init as initUiMetric } from '../application/services/ui_metric'; import { init as initNotification } from '../application/services/notification'; import { retryLifecycleForIndex } from '../application/services/api'; @@ -238,7 +237,7 @@ export const ilmFilterExtension = indices => { } }; -export const addAllExtensions = () => { +export const addAllExtensions = extensionsService => { extensionsService.addAction(retryLifecycleActionExtension); extensionsService.addAction(removeLifecyclePolicyActionExtension); extensionsService.addAction(addLifecyclePolicyActionExtension); diff --git a/x-pack/legacy/plugins/index_management/index.ts b/x-pack/legacy/plugins/index_management/index.ts index c92b38c0d94be8..9eba98a526d2bc 100644 --- a/x-pack/legacy/plugins/index_management/index.ts +++ b/x-pack/legacy/plugins/index_management/index.ts @@ -4,36 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { resolve } from 'path'; -import { Legacy } from 'kibana'; -import { PLUGIN } from './common/constants'; -import { plugin as initServerPlugin, Dependencies } from './server'; - -export type ServerFacade = Legacy.Server; - export function indexManagement(kibana: any) { return new kibana.Plugin({ - id: PLUGIN.id, + id: 'index_management', configPrefix: 'xpack.index_management', - publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'elasticsearch', 'xpack_main'], - - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - managementSections: ['plugins/index_management'], - }, - - init(server: ServerFacade) { - const coreSetup = server.newPlatform.setup.core; - const coreInitializerContext = server.newPlatform.coreContext; - const pluginsSetup: Dependencies = { - licensing: server.newPlatform.setup.plugins.licensing as any, - }; - - const serverPlugin = initServerPlugin(coreInitializerContext as any); - const serverPublicApi = serverPlugin.setup(coreSetup, pluginsSetup); - - server.expose('addIndexManagementDataEnricher', serverPublicApi.indexDataEnricher.add); - }, }); } diff --git a/x-pack/legacy/plugins/index_management/public/index.html b/x-pack/legacy/plugins/index_management/public/index.html deleted file mode 100644 index 0e9ac6fa0bc977..00000000000000 --- a/x-pack/legacy/plugins/index_management/public/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/legacy/plugins/remote_clusters/index.ts b/x-pack/legacy/plugins/remote_clusters/index.ts index 37b2224f8d7c25..439cb818d8a562 100644 --- a/x-pack/legacy/plugins/remote_clusters/index.ts +++ b/x-pack/legacy/plugins/remote_clusters/index.ts @@ -5,6 +5,8 @@ */ import { resolve } from 'path'; +import { Legacy } from 'kibana'; + import { PLUGIN } from './common'; export function remoteClusters(kibana: any) { @@ -28,7 +30,7 @@ export function remoteClusters(kibana: any) { enabled: Joi.boolean().default(true), }).default(); }, - isEnabled(config: any) { + isEnabled(config: Legacy.KibanaConfig) { return ( config.get('xpack.remote_clusters.enabled') && config.get('xpack.index_management.enabled') ); diff --git a/x-pack/legacy/plugins/rollup/index.ts b/x-pack/legacy/plugins/rollup/index.ts index 7548af23b3aae9..2c8363cc397f45 100644 --- a/x-pack/legacy/plugins/rollup/index.ts +++ b/x-pack/legacy/plugins/rollup/index.ts @@ -40,7 +40,7 @@ export function rollup(kibana: any) { }, init(server: any) { const { core: coreSetup, plugins } = server.newPlatform.setup; - const { usageCollection, metrics } = plugins; + const { usageCollection, metrics, indexManagement } = plugins; const rollupSetup = (plugins.rollup as unknown) as RollupSetup; @@ -54,11 +54,11 @@ export function rollup(kibana: any) { rollupPluginInstance.setup(coreSetup, { usageCollection, metrics, + indexManagement, __LEGACY: { plugins: { xpack_main: server.plugins.xpack_main, rollup: server.plugins[PLUGIN.ID], - index_management: server.plugins.index_management, }, }, }); diff --git a/x-pack/legacy/plugins/rollup/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts index 64eb1f64363892..e3e663ac7b0f44 100644 --- a/x-pack/legacy/plugins/rollup/public/legacy.ts +++ b/x-pack/legacy/plugins/rollup/public/legacy.ts @@ -10,7 +10,6 @@ import { aggTypeFieldFilters } from 'ui/agg_types'; import { addSearchStrategy } from '../../../../../src/plugins/data/public'; import { RollupPlugin } from './plugin'; import { setup as management } from '../../../../../src/legacy/core_plugins/management/public/legacy'; -import { extensionsService } from '../../index_management/public'; const plugin = new RollupPlugin(); @@ -20,7 +19,6 @@ export const setup = plugin.setup(npSetup.core, { aggTypeFilters, aggTypeFieldFilters, addSearchStrategy, - indexManagementExtensions: extensionsService, managementLegacy: management, }, }); diff --git a/x-pack/legacy/plugins/rollup/public/plugin.ts b/x-pack/legacy/plugins/rollup/public/plugin.ts index 90d7e2d9d01919..a01383f4733ef8 100644 --- a/x-pack/legacy/plugins/rollup/public/plugin.ts +++ b/x-pack/legacy/plugins/rollup/public/plugin.ts @@ -27,7 +27,7 @@ import { // @ts-ignore import { CRUD_APP_BASE_PATH } from './crud_app/constants'; import { ManagementSetup } from '../../../../../src/plugins/management/public'; -import { IndexMgmtSetup } from '../../index_management/public'; +import { IndexMgmtSetup } from '../../../../plugins/index_management/public'; // @ts-ignore import { setEsBaseAndXPackBase, setHttp } from './crud_app/services'; import { setNotifications, setFatalErrors } from './kibana_services'; @@ -39,30 +39,28 @@ export interface RollupPluginSetupDependencies { aggTypeFieldFilters: AggTypeFieldFilters; addSearchStrategy: (searchStrategy: SearchStrategyProvider) => void; managementLegacy: ManagementSetupLegacy; - indexManagementExtensions: IndexMgmtSetup['extensionsService']; }; home?: HomePublicPluginSetup; management: ManagementSetup; + indexManagement?: IndexMgmtSetup; } export class RollupPlugin implements Plugin { setup( core: CoreSetup, { - __LEGACY: { - aggTypeFilters, - aggTypeFieldFilters, - addSearchStrategy, - managementLegacy, - indexManagementExtensions, - }, + __LEGACY: { aggTypeFilters, aggTypeFieldFilters, addSearchStrategy, managementLegacy }, home, management, + indexManagement, }: RollupPluginSetupDependencies ) { setFatalErrors(core.fatalErrors); - indexManagementExtensions.addBadge(rollupBadgeExtension); - indexManagementExtensions.addToggle(rollupToggleExtension); + + if (indexManagement) { + indexManagement.extensionsService.addBadge(rollupBadgeExtension); + indexManagement.extensionsService.addToggle(rollupToggleExtension); + } const isRollupIndexPatternsEnabled = core.uiSettings.get(CONFIG_ROLLUPS); diff --git a/x-pack/legacy/plugins/rollup/server/plugin.ts b/x-pack/legacy/plugins/rollup/server/plugin.ts index 52b1e31af4eb22..090cb8a47377a1 100644 --- a/x-pack/legacy/plugins/rollup/server/plugin.ts +++ b/x-pack/legacy/plugins/rollup/server/plugin.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server'; +import { IndexMgmtSetup } from '../../../../plugins/index_management/server'; import { registerLicenseChecker } from '../../../server/lib/register_license_checker'; import { PLUGIN } from '../common'; import { ServerShim, RouteDependencies } from './types'; @@ -38,10 +39,12 @@ export class RollupsServerPlugin implements Plugin { __LEGACY: serverShim, usageCollection, metrics, + indexManagement, }: { __LEGACY: ServerShim; usageCollection?: UsageCollectionSetup; metrics?: VisTypeTimeseriesSetup; + indexManagement?: IndexMgmtSetup; } ) { const elasticsearch = await elasticsearchService.adminClient; @@ -76,11 +79,8 @@ export class RollupsServerPlugin implements Plugin { }); } - if ( - serverShim.plugins.index_management && - serverShim.plugins.index_management.addIndexManagementDataEnricher - ) { - serverShim.plugins.index_management.addIndexManagementDataEnricher(rollupDataEnricher); + if (indexManagement && indexManagement.indexDataEnricher) { + indexManagement.indexDataEnricher.add(rollupDataEnricher); } if (metrics) { diff --git a/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts index 7c5e160c54a31a..ad621f2d9ba806 100644 --- a/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts +++ b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -interface Index { - name: string; - [key: string]: unknown; -} +import { Index } from '../../../../plugins/index_management/server'; export const rollupDataEnricher = async (indicesList: Index[], callWithRequest: any) => { if (!indicesList || !indicesList.length) { - return indicesList; + return Promise.resolve(indicesList); } const params = { diff --git a/x-pack/legacy/plugins/rollup/server/types.ts b/x-pack/legacy/plugins/rollup/server/types.ts index 62a4841133cffb..bcc6770e9b8ee0 100644 --- a/x-pack/legacy/plugins/rollup/server/types.ts +++ b/x-pack/legacy/plugins/rollup/server/types.ts @@ -11,7 +11,6 @@ export interface ServerShim { plugins: { xpack_main: XPackMainPlugin; rollup: any; - index_management: any; }; } diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/constants.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/constants.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts similarity index 96% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts index e5f0b25d89c3e1..7e3e1fba9c44a6 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts @@ -12,10 +12,10 @@ import { TestBedConfig, findTestSubject, nextTick, -} from '../../../../../../test_utils'; -import { IndexManagementHome } from '../../../public/application/sections/home'; +} from '../../../../../test_utils'; +import { IndexManagementHome } from '../../../public/application/sections/home'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { BASE_PATH } from '../../../common/constants'; -import { indexManagementStore } from '../../../public/application/store'; +import { indexManagementStore } from '../../../public/application/store'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { Template } from '../../../common/types'; import { WithAppDependencies, services } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts similarity index 95% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts index 6dce4453a67f93..66021b531919a9 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts @@ -9,7 +9,7 @@ import { setup as templateCreateSetup } from './template_create.helpers'; import { setup as templateCloneSetup } from './template_clone.helpers'; import { setup as templateEditSetup } from './template_edit.helpers'; -export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../../test_utils'; +export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../test_utils'; export { setupEnvironment } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx similarity index 95% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index 0212efe1f322d3..1eaf7efd173954 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @kbn/eslint/no-restricted-paths */ import React from 'react'; import axios from 'axios'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; @@ -10,7 +11,7 @@ import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import { notificationServiceMock, docLinksServiceMock, -} from '../../../../../../../src/core/public/mocks'; +} from '../../../../../../src/core/public/mocks'; import { AppContextProvider } from '../../../public/application/app_context'; import { httpService } from '../../../public/application/services/http'; import { breadcrumbService } from '../../../public/application/services/breadcrumbs'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts similarity index 85% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts index cd1b67c08d9345..36498b99ba1435 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; +import { registerTestBed, TestBedConfig } from '../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateClone } from '../../../public/application/sections/template_clone'; +import { TemplateClone } from '../../../public/application/sections/template_clone'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { formSetup } from './template_form.helpers'; import { TEMPLATE_NAME } from './constants'; import { WithAppDependencies } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts similarity index 84% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts index 8e62bc25d6bd1d..14a44968a93c32 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; +import { registerTestBed, TestBedConfig } from '../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateCreate } from '../../../public/application/sections/template_create'; +import { TemplateCreate } from '../../../public/application/sections/template_create'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { formSetup, TestSubjects } from './template_form.helpers'; import { WithAppDependencies } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts similarity index 85% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts index cb1025234b48e9..af5fa8b79ecad6 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; +import { registerTestBed, TestBedConfig } from '../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateEdit } from '../../../public/application/sections/template_edit'; +import { TemplateEdit } from '../../../public/application/sections/template_edit'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { formSetup, TestSubjects } from './template_form.helpers'; import { TEMPLATE_NAME } from './constants'; import { WithAppDependencies } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts similarity index 99% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts index a7c87723b33fb8..134c67c278b220 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TestBed, SetupFunc, UnwrapPromise } from '../../../../../../test_utils'; +import { TestBed, SetupFunc, UnwrapPromise } from '../../../../../test_utils'; import { Template } from '../../../common/types'; import { nextTick } from './index'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/home.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/home.test.ts rename to x-pack/plugins/index_management/__jest__/client_integration/home.test.ts diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_clone.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_clone.test.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_create.test.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_edit.test.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx diff --git a/x-pack/legacy/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap b/x-pack/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap rename to x-pack/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap diff --git a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js b/x-pack/plugins/index_management/__jest__/components/index_table.test.js similarity index 98% rename from x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js rename to x-pack/plugins/index_management/__jest__/components/index_table.test.js index 2b4fd894364580..15c3ef0b845624 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js +++ b/x-pack/plugins/index_management/__jest__/components/index_table.test.js @@ -20,13 +20,13 @@ import { setUiMetricService } from '../../public/application/services/api'; import { indexManagementStore } from '../../public/application/store'; import { setExtensionsService } from '../../public/application/store/selectors'; import { BASE_PATH, API_BASE_PATH } from '../../common/constants'; -import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers'; +import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; import { ExtensionsService } from '../../public/services'; import sinon from 'sinon'; import { findTestSubject } from '@elastic/eui/lib/test'; /* eslint-disable @kbn/eslint/no-restricted-paths */ -import { notificationServiceMock } from '../../../../../../src/core/public/notifications/notifications_service.mock'; +import { notificationServiceMock } from '../../../../../src/core/public/notifications/notifications_service.mock'; jest.mock('ui/new_platform'); diff --git a/x-pack/legacy/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap b/x-pack/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap rename to x-pack/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap diff --git a/x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js b/x-pack/plugins/index_management/__jest__/lib/flatten_object.test.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js rename to x-pack/plugins/index_management/__jest__/lib/flatten_object.test.js diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ace.js b/x-pack/plugins/index_management/__mocks__/ace.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__mocks__/ace.js rename to x-pack/plugins/index_management/__mocks__/ace.js diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ui/documentation_links.js b/x-pack/plugins/index_management/__mocks__/ui/documentation_links.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__mocks__/ui/documentation_links.js rename to x-pack/plugins/index_management/__mocks__/ui/documentation_links.js diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ui/notify.js b/x-pack/plugins/index_management/__mocks__/ui/notify.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__mocks__/ui/notify.js rename to x-pack/plugins/index_management/__mocks__/ui/notify.js diff --git a/x-pack/legacy/plugins/index_management/common/constants/api_base_path.ts b/x-pack/plugins/index_management/common/constants/api_base_path.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/api_base_path.ts rename to x-pack/plugins/index_management/common/constants/api_base_path.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/base_path.ts b/x-pack/plugins/index_management/common/constants/base_path.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/base_path.ts rename to x-pack/plugins/index_management/common/constants/base_path.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/index.ts rename to x-pack/plugins/index_management/common/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/index_statuses.ts b/x-pack/plugins/index_management/common/constants/index_statuses.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/index_statuses.ts rename to x-pack/plugins/index_management/common/constants/index_statuses.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/invalid_characters.ts b/x-pack/plugins/index_management/common/constants/invalid_characters.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/invalid_characters.ts rename to x-pack/plugins/index_management/common/constants/invalid_characters.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/plugin.ts b/x-pack/plugins/index_management/common/constants/plugin.ts similarity index 86% rename from x-pack/legacy/plugins/index_management/common/constants/plugin.ts rename to x-pack/plugins/index_management/common/constants/plugin.ts index 2cd137f62d3dbf..e25f537edcf8da 100644 --- a/x-pack/legacy/plugins/index_management/common/constants/plugin.ts +++ b/x-pack/plugins/index_management/common/constants/plugin.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LicenseType } from '../../../../../plugins/licensing/common/types'; +import { LicenseType } from '../../../licensing/common/types'; const basicLicense: LicenseType = 'basic'; diff --git a/x-pack/legacy/plugins/index_management/common/constants/ui_metric.ts b/x-pack/plugins/index_management/common/constants/ui_metric.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/ui_metric.ts rename to x-pack/plugins/index_management/common/constants/ui_metric.ts diff --git a/x-pack/legacy/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/index.ts rename to x-pack/plugins/index_management/common/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/lib/index.ts b/x-pack/plugins/index_management/common/lib/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/lib/index.ts rename to x-pack/plugins/index_management/common/lib/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/lib/template_serialization.ts rename to x-pack/plugins/index_management/common/lib/template_serialization.ts diff --git a/x-pack/legacy/plugins/index_management/common/types/index.ts b/x-pack/plugins/index_management/common/types/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/types/index.ts rename to x-pack/plugins/index_management/common/types/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/types/templates.ts rename to x-pack/plugins/index_management/common/types/templates.ts diff --git a/x-pack/plugins/index_management/kibana.json b/x-pack/plugins/index_management/kibana.json new file mode 100644 index 00000000000000..7387a042988c01 --- /dev/null +++ b/x-pack/plugins/index_management/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "indexManagement", + "version": "kibana", + "server": true, + "ui": true, + "requiredPlugins": [ + "home", + "licensing", + "management" + ], + "optionalPlugins": [ + "usageCollection" + ], + "configPath": ["xpack", "index_management"] +} diff --git a/x-pack/legacy/plugins/index_management/public/application/app.tsx b/x-pack/plugins/index_management/public/application/app.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/app.tsx rename to x-pack/plugins/index_management/public/application/app.tsx index 3b475f3baba04a..83997dd6ece184 100644 --- a/x-pack/legacy/plugins/index_management/public/application/app.tsx +++ b/x-pack/plugins/index_management/public/application/app.tsx @@ -16,7 +16,7 @@ import { useServices } from './app_context'; export const App = () => { const { uiMetricService } = useServices(); - useEffect(() => uiMetricService.trackMetric('loaded', UIM_APP_LOAD), []); + useEffect(() => uiMetricService.trackMetric('loaded', UIM_APP_LOAD), [uiMetricService]); return ( diff --git a/x-pack/legacy/plugins/index_management/public/application/app_context.tsx b/x-pack/plugins/index_management/public/application/app_context.tsx similarity index 90% rename from x-pack/legacy/plugins/index_management/public/application/app_context.tsx rename to x-pack/plugins/index_management/public/application/app_context.tsx index 12e0d362a29302..2bb618ad8efceb 100644 --- a/x-pack/legacy/plugins/index_management/public/application/app_context.tsx +++ b/x-pack/plugins/index_management/public/application/app_context.tsx @@ -5,9 +5,9 @@ */ import React, { createContext, useContext } from 'react'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart } from '../../../../../src/core/public'; -import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/public'; +import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public'; import { IndexMgmtMetricsType } from '../types'; import { UiMetricService, NotificationService, HttpService } from './services'; import { ExtensionsService } from '../services'; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/index.ts b/x-pack/plugins/index_management/public/application/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/index.ts rename to x-pack/plugins/index_management/public/application/components/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts similarity index 90% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts index e3313bfba56fd9..6d64cb73da4bdd 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts @@ -10,7 +10,7 @@ export { getRandomString, findTestSubject, TestBed, -} from '../../../../../../../../../../test_utils'; +} from '../../../../../../../../../test_utils'; export const componentHelpers = { mappingsEditor: { setup: mappingsEditorSetup }, diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts similarity index 85% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts index dcee956130a29f..acb416654023eb 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed } from '../../../../../../../../../../test_utils'; +import { registerTestBed } from '../../../../../../../../../test_utils'; import { MappingsEditor } from '../../../mappings_editor'; export const setup = (props: any) => diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx index 0c5c9e2a15b751..9b0b8420f9ea93 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx @@ -110,7 +110,7 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => { }); }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); useEffect(() => { if (didMountRef.current) { @@ -121,14 +121,14 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => { // Avoid reseting the form on component mount. didMountRef.current = true; } - }, [defaultValue]); + }, [defaultValue, form]); useEffect(() => { return () => { // On unmount => save in the state a snapshot of the current form data. dispatch({ type: 'configuration.save' }); }; - }, []); + }, [dispatch]); return ( diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx similarity index 91% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx index 378d669dee69cb..400de4052afa4b 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx @@ -24,7 +24,7 @@ export const DocumentFields = React.memo(() => { if (editorType === 'json') { return deNormalize(fields); } - }, [editorType]); + }, [editorType, fields]); const editor = editorType === 'json' ? ( @@ -41,9 +41,12 @@ export const DocumentFields = React.memo(() => { return ; }; - const onSearchChange = useCallback((value: string) => { - dispatch({ type: 'search:update', value }); - }, []); + const onSearchChange = useCallback( + (value: string) => { + dispatch({ type: 'search:update', value }); + }, + [dispatch] + ); const searchTerm = search.term.trim(); diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx similarity index 86% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx index de3d70db31af4d..a91231352c1684 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx @@ -56,15 +56,21 @@ export const AnalyzerParameterSelects = ({ }); return subscription.unsubscribe; - }, [form]); + }, [form, onChange]); - const getSubOptionsMeta = (mainValue: string) => - mapOptionsToSubOptions !== undefined ? mapOptionsToSubOptions[mainValue] : undefined; + const getSubOptionsMeta = useCallback( + (mainValue: string) => + mapOptionsToSubOptions !== undefined ? mapOptionsToSubOptions[mainValue] : undefined, + [mapOptionsToSubOptions] + ); - const onMainValueChange = useCallback((mainValue: unknown) => { - const subOptionsMeta = getSubOptionsMeta(mainValue as string); - form.setFieldValue('sub', subOptionsMeta ? subOptionsMeta.options[0].value : undefined); - }, []); + const onMainValueChange = useCallback( + (mainValue: unknown) => { + const subOptionsMeta = getSubOptionsMeta(mainValue as string); + form.setFieldValue('sub', subOptionsMeta ? subOptionsMeta.options[0].value : undefined); + }, + [form, getSubOptionsMeta] + ); const renderSelect = (field: FieldHook, opts: Options) => { const isSuperSelect = areOptionsSuperSelect(opts); diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx similarity index 83% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx index 5f1b8c0df770ea..60b025ce644efe 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx @@ -66,7 +66,7 @@ export const CreateField = React.memo(function CreateFieldComponent({ }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); const cancel = () => { dispatch({ type: 'documentField.changeStatus', value: 'idle' }); @@ -108,43 +108,53 @@ export const CreateField = React.memo(function CreateFieldComponent({ * * @param type The selected field type */ - const getSubTypeMeta = ( - type: MainType - ): { subTypeLabel?: string; subTypeOptions?: ComboBoxOption[] } => { - const typeDefinition = TYPE_DEFINITION[type]; - const hasSubTypes = typeDefinition !== undefined && typeDefinition.subTypes; - - let subTypeOptions = hasSubTypes - ? typeDefinition - .subTypes!.types.map(subType => TYPE_DEFINITION[subType]) - .map( - subType => ({ value: subType.value as SubType, label: subType.label } as ComboBoxOption) - ) - : undefined; - - if (hasSubTypes) { - if (isMultiField) { - // If it is a multi-field, we need to filter out non-allowed types - subTypeOptions = filterTypesForMultiField(subTypeOptions!); - } else if (isRootLevelField === false) { - subTypeOptions = filterTypesForNonRootFields(subTypeOptions!); + const getSubTypeMeta = useCallback( + ( + type: MainType + ): { + subTypeLabel?: string; + subTypeOptions?: ComboBoxOption[]; + } => { + const typeDefinition = TYPE_DEFINITION[type]; + const hasSubTypes = typeDefinition !== undefined && typeDefinition.subTypes; + + let subTypeOptions = hasSubTypes + ? typeDefinition + .subTypes!.types.map(subType => TYPE_DEFINITION[subType]) + .map( + subType => + ({ value: subType.value as SubType, label: subType.label } as ComboBoxOption) + ) + : undefined; + + if (hasSubTypes) { + if (isMultiField) { + // If it is a multi-field, we need to filter out non-allowed types + subTypeOptions = filterTypesForMultiField(subTypeOptions!); + } else if (isRootLevelField === false) { + subTypeOptions = filterTypesForNonRootFields(subTypeOptions!); + } } - } - return { - subTypeOptions, - subTypeLabel: hasSubTypes ? typeDefinition.subTypes!.label : undefined, - }; - }; + return { + subTypeOptions, + subTypeLabel: hasSubTypes ? typeDefinition.subTypes!.label : undefined, + }; + }, + [isMultiField, isRootLevelField] + ); - const onTypeChange = (nextType: ComboBoxOption[]) => { - form.setFieldValue('type', nextType); + const onTypeChange = useCallback( + (nextType: ComboBoxOption[]) => { + form.setFieldValue('type', nextType); - if (nextType.length) { - const { subTypeOptions } = getSubTypeMeta(nextType[0].value as MainType); - form.setFieldValue('subType', subTypeOptions ? [subTypeOptions[0]] : undefined); - } - }; + if (nextType.length) { + const { subTypeOptions } = getSubTypeMeta(nextType[0].value as MainType); + form.setFieldValue('subType', subTypeOptions ? [subTypeOptions[0]] : undefined); + } + }, + [form, getSubTypeMeta] + ); const renderFormFields = useCallback( ({ type }) => { @@ -208,7 +218,7 @@ export const CreateField = React.memo(function CreateFieldComponent({
); }, - [form, isMultiField] + [getSubTypeMeta, isMultiField, isRootLevelField, onTypeChange] ); const renderFormActions = () => ( diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx similarity index 97% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx index 284ae8803acb56..1f77584439568c 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx @@ -32,11 +32,11 @@ export const EditFieldContainer = React.memo(({ field, allFields }: Props) => { }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); const exitEdit = useCallback(() => { dispatch({ type: 'documentField.changeStatus', value: 'idle' }); - }, []); + }, [dispatch]); return ; }); diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx similarity index 92% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx index cff2d294fead95..55093e606cfa1f 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx @@ -23,7 +23,7 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop fields: { byId, maxNestedDepth }, } = useMappingsState(); - const getField = (id: string) => byId[id]; + const getField = useCallback((id: string) => byId[id], [byId]); const field: NormalizedField = getField(fieldId); const { childFields } = field; @@ -33,7 +33,7 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop const areActionButtonsVisible = status === 'idle'; const childFieldsArray = useMemo( () => (childFields !== undefined ? childFields.map(getField) : []), - [childFields] + [childFields, getField] ); const addField = useCallback(() => { @@ -41,18 +41,18 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop type: 'documentField.createField', value: fieldId, }); - }, [fieldId]); + }, [fieldId, dispatch]); const editField = useCallback(() => { dispatch({ type: 'documentField.editField', value: fieldId, }); - }, [fieldId]); + }, [fieldId, dispatch]); const toggleExpand = useCallback(() => { dispatch({ type: 'field.toggleExpand', value: { fieldId } }); - }, [fieldId]); + }, [fieldId, dispatch]); return ( { documentFields: { status, fieldToAddFieldTo }, } = useMappingsState(); - const getField = (fieldId: string) => byId[fieldId]; - const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields]); + const getField = useCallback((fieldId: string) => byId[fieldId], [byId]); + const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields, getField]); - const addField = () => { + const addField = useCallback(() => { dispatch({ type: 'documentField.createField' }); - }; + }, [dispatch]); useEffect(() => { /** @@ -32,7 +32,7 @@ export const DocumentFieldsTreeEditor = () => { if (status === 'idle' && fields.length === 0) { addField(); } - }, [fields, status]); + }, [addField, fields, status]); const renderCreateField = () => { // The "fieldToAddFieldTo" is undefined when adding to the top level "properties" object. diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx index a9433d3a7530ff..f8e3eca7898d24 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx @@ -19,7 +19,7 @@ jest.mock('@elastic/eui', () => ({ ), })); -import { registerTestBed, nextTick, TestBed } from '../../../../../../../../../test_utils'; +import { registerTestBed, nextTick, TestBed } from '../../../../../../../../test_utils'; import { LoadMappingsProvider } from './load_mappings_provider'; const ComponentToTest = ({ onJson }: { onJson: () => void }) => ( diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx index 471217108ba6f5..f32fcb3956e1c3 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx @@ -69,7 +69,7 @@ export const TemplatesForm = React.memo(({ defaultValue }: Props) => { }); }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); useEffect(() => { if (didMountRef.current) { @@ -80,14 +80,14 @@ export const TemplatesForm = React.memo(({ defaultValue }: Props) => { // Avoid reseting the form on component mount. didMountRef.current = true; } - }, [defaultValue]); + }, [defaultValue, form]); useEffect(() => { return () => { // On unmount => save in the state a snapshot of the current form data. dispatch({ type: 'templates.save' }); }; - }, []); + }, [dispatch]); return ( <> diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx index d79a023386e8d6..b6345a7140e15b 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx @@ -78,7 +78,7 @@ export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSetting isValid: true, }); } - }, [multipleMappingsDeclared]); + }, [multipleMappingsDeclared, onUpdate, defaultValue]); const changeTab = async (tab: TabName, state: State) => { if (selectedTab === 'advanced') { diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx index 65a1aa2858d142..247bd183baddf5 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx @@ -168,7 +168,7 @@ export const MappingsState = React.memo(({ children, onUpdate, defaultValue }: P }, isValid: state.isValid, }); - }, [state]); + }, [state, onUpdate]); useEffect(() => { /** @@ -187,7 +187,7 @@ export const MappingsState = React.memo(({ children, onUpdate, defaultValue }: P } else { didMountRef.current = true; } - }, [defaultValue]); + }, [defaultValue, parsedFieldsDefaultValue]); return ( diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts similarity index 72% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts index e99d8840d57dfe..2979015c07455d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts @@ -24,7 +24,7 @@ export { VALIDATION_TYPES, ValidationFunc, ValidationFuncArg, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; +} from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; export { CheckBoxField, @@ -39,14 +39,14 @@ export { ComboBoxField, ToggleField, JsonEditorField, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/components'; +} from '../../../../../../../src/plugins/es_ui_shared/static/forms/components'; export { fieldFormatters, fieldValidators, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +} from '../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; export { JsonEditor, OnJsonEditorUpdateHandler, -} from '../../../../../../../../src/plugins/es_ui_shared/public'; +} from '../../../../../../../src/plugins/es_ui_shared/public'; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/types.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts b/x-pack/plugins/index_management/public/application/components/no_match/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts rename to x-pack/plugins/index_management/public/application/components/no_match/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx b/x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx rename to x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts b/x-pack/plugins/index_management/public/application/components/page_error/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts rename to x-pack/plugins/index_management/public/application/components/page_error/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx b/x-pack/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx rename to x-pack/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx b/x-pack/plugins/index_management/public/application/components/section_error.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx rename to x-pack/plugins/index_management/public/application/components/section_error.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx b/x-pack/plugins/index_management/public/application/components/section_loading.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx rename to x-pack/plugins/index_management/public/application/components/section_loading.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx b/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx rename to x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts b/x-pack/plugins/index_management/public/application/components/template_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts rename to x-pack/plugins/index_management/public/application/components/template_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts rename to x-pack/plugins/index_management/public/application/components/template_form/steps/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx similarity index 94% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx index dd8d49a5690425..2f6e055b5d0c6d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx @@ -7,15 +7,8 @@ import React, { useEffect } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { - useForm, - Form, - getUseField, -} from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; -import { - getFormRow, - Field, -} from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/components'; + +import { useForm, Form, getUseField, getFormRow, Field } from '../../../../shared_imports'; import { documentationService } from '../../../services/documentation'; import { StepProps } from '../types'; import { schemas, nameConfig, nameConfigWithoutValidations } from '../template_form_schemas'; @@ -80,11 +73,11 @@ export const StepLogistics: React.FunctionComponent = ({ useEffect(() => { onStepValidityChange(form.isValid); - }, [form.isValid]); + }, [form.isValid, onStepValidityChange]); useEffect(() => { setDataGetter(form.submit); - }, [form]); + }, [form.submit, setDataGetter]); const { name, indexPatterns, order, version } = fieldsMeta; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx index 09172bf5cd0caa..09da43b83c3c51 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx @@ -20,7 +20,7 @@ import { EuiCodeBlock, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { serializers } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +import { serializers } from '../../../../shared_imports'; import { serializeTemplate } from '../../../../../common/lib/template_serialization'; import { Template } from '../../../../../common/types'; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts similarity index 83% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts rename to x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts index ae16a2e9263ff5..fbe479ea0cf235 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useState } from 'react'; +import { useEffect, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { isJSON } from '../../../../../../../../../src/plugins/es_ui_shared/static/validators/string'; +import { isJSON } from '../../../../shared_imports'; import { StepProps } from '../types'; interface Parameters { @@ -29,7 +29,7 @@ export const useJsonStep = ({ const [content, setContent] = useState(stringifyJson(defaultValue)); const [error, setError] = useState(null); - const validateContent = () => { + const validateContent = useCallback(() => { // We allow empty string as it will be converted to "{}"" const isValid = content.trim() === '' ? true : isJSON(content); if (!isValid) { @@ -42,20 +42,20 @@ export const useJsonStep = ({ setError(null); } return isValid; - }; + }, [content]); - const dataGetter = () => { + const dataGetter = useCallback(() => { const isValid = validateContent(); const value = isValid && content.trim() !== '' ? JSON.parse(content) : {}; const data = { [prop]: value }; return Promise.resolve({ isValid, data }); - }; + }, [content, validateContent, prop]); useEffect(() => { const isValid = validateContent(); onStepValidityChange(isValid); setDataGetter(dataGetter); - }, [content]); + }, [content, dataGetter, onStepValidityChange, setDataGetter, validateContent]); return { content, diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx similarity index 92% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx index 6a76e1d203b70f..58be9b2c633653 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, useState, useRef } from 'react'; +import React, { Fragment, useState, useRef, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, @@ -14,7 +14,7 @@ import { EuiSpacer, } from '@elastic/eui'; -import { serializers } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +import { serializers } from '../../../shared_imports'; import { Template } from '../../../../common/types'; import { TemplateSteps } from './template_steps'; import { StepAliases, StepLogistics, StepMappings, StepSettings, StepReview } from './steps'; @@ -70,19 +70,25 @@ export const TemplateForm: React.FunctionComponent = ({ const CurrentStepComponent = stepComponentMap[currentStep]; const isStepValid = validation[currentStep].isValid; - const setStepDataGetter = (stepDataGetter: DataGetterFunc) => { - stepsDataGetters.current[currentStep] = stepDataGetter; - }; + const setStepDataGetter = useCallback( + (stepDataGetter: DataGetterFunc) => { + stepsDataGetters.current[currentStep] = stepDataGetter; + }, + [currentStep] + ); - const onStepValidityChange = (isValid: boolean | undefined) => { - setValidation(prev => ({ - ...prev, - [currentStep]: { - isValid, - errors: {}, - }, - })); - }; + const onStepValidityChange = useCallback( + (isValid: boolean | undefined) => { + setValidation(prev => ({ + ...prev, + [currentStep]: { + isValid, + errors: {}, + }, + })); + }, + [currentStep] + ); const validateAndGetDataFromCurrentStep = async () => { const validateAndGetData = stepsDataGetters.current[currentStep]; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx index ed2616cc64e381..9ff73b71adf50d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx @@ -13,12 +13,9 @@ import { FIELD_TYPES, VALIDATION_TYPES, FieldConfig, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; - -import { fieldFormatters, fieldValidators, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +} from '../../../shared_imports'; import { INVALID_INDEX_PATTERN_CHARS, diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_steps.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/template_steps.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts b/x-pack/plugins/index_management/public/application/components/template_form/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts rename to x-pack/plugins/index_management/public/application/components/template_form/types.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts b/x-pack/plugins/index_management/public/application/constants/detail_panel_tabs.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts rename to x-pack/plugins/index_management/public/application/constants/detail_panel_tabs.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/index.ts b/x-pack/plugins/index_management/public/application/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/constants/index.ts rename to x-pack/plugins/index_management/public/application/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts b/x-pack/plugins/index_management/public/application/constants/refresh_intervals.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts rename to x-pack/plugins/index_management/public/application/constants/refresh_intervals.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/index.tsx b/x-pack/plugins/index_management/public/application/index.tsx similarity index 94% rename from x-pack/legacy/plugins/index_management/public/application/index.tsx rename to x-pack/plugins/index_management/public/application/index.tsx index b9859903f14345..5850cb8d42f1aa 100644 --- a/x-pack/legacy/plugins/index_management/public/application/index.tsx +++ b/x-pack/plugins/index_management/public/application/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart } from '../../../../../src/core/public'; import { AppContextProvider, AppDependencies } from './app_context'; import { App } from './app'; diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/ace.js b/x-pack/plugins/index_management/public/application/lib/ace.js similarity index 94% rename from x-pack/legacy/plugins/index_management/public/application/lib/ace.js rename to x-pack/plugins/index_management/public/application/lib/ace.js index b9620dfbdb1207..3b37c8fb8870ed 100644 --- a/x-pack/legacy/plugins/index_management/public/application/lib/ace.js +++ b/x-pack/plugins/index_management/public/application/lib/ace.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import ace from 'ace'; +import brace from 'brace'; import 'brace/ext/language_tools'; const splitTokens = line => { @@ -43,14 +43,14 @@ const wordCompleter = words => { }; export const createAceEditor = (div, value, readOnly = true, autocompleteArray) => { - const editor = ace.edit(div); + const editor = brace.edit(div); editor.$blockScrolling = Infinity; editor.setValue(value, -1); const session = editor.getSession(); session.setUseWrapMode(true); session.setMode('ace/mode/json'); if (autocompleteArray) { - const languageTools = ace.acequire('ace/ext/language_tools'); + const languageTools = brace.acequire('ace/ext/language_tools'); const autocompleter = wordCompleter(autocompleteArray); languageTools.setCompleters([autocompleter]); } diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js b/x-pack/plugins/index_management/public/application/lib/edit_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js rename to x-pack/plugins/index_management/public/application/lib/edit_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js b/x-pack/plugins/index_management/public/application/lib/flatten_object.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js rename to x-pack/plugins/index_management/public/application/lib/flatten_object.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js b/x-pack/plugins/index_management/public/application/lib/flatten_panel_tree.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js rename to x-pack/plugins/index_management/public/application/lib/flatten_panel_tree.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js b/x-pack/plugins/index_management/public/application/lib/index_status_labels.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js rename to x-pack/plugins/index_management/public/application/lib/index_status_labels.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts b/x-pack/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts rename to x-pack/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js b/x-pack/plugins/index_management/public/application/lib/render_badges.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js rename to x-pack/plugins/index_management/public/application/lib/render_badges.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx b/x-pack/plugins/index_management/public/application/sections/home/home.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx rename to x-pack/plugins/index_management/public/application/sections/home/home.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts b/x-pack/plugins/index_management/public/application/sections/home/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts b/x-pack/plugins/index_management/public/application/sections/home/index_list/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx index ced8bd97e744b9..9c31b0d650449d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx @@ -32,7 +32,7 @@ import { } from '../../../../../../common/constants'; import { Template } from '../../../../../../common/types'; import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components'; -import { loadIndexTemplate } from '../../../../services/api'; +import { useLoadIndexTemplate } from '../../../../services/api'; import { decodePath } from '../../../../services/routing'; import { SendRequestResponse } from '../../../../../shared_imports'; import { useServices } from '../../../../app_context'; @@ -103,7 +103,7 @@ export const TemplateDetails: React.FunctionComponent = ({ }) => { const { uiMetricService } = useServices(); const decodedTemplateName = decodePath(templateName); - const { error, data: templateDetails, isLoading } = loadIndexTemplate(decodedTemplateName); + const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(decodedTemplateName); // TS complains if we use destructuring here. Fixed in 3.6.0 (https://github.com/microsoft/TypeScript/pull/31711). const isManaged = templateDetails ? templateDetails.isManaged : undefined; const [templateToDelete, setTemplateToDelete] = useState>([]); diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx similarity index 97% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx index 71c32e2e0177ff..ffdb224f162713 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { SectionError, SectionLoading, Error } from '../../../components'; import { TemplateTable } from './template_table'; -import { loadIndexTemplates } from '../../../services/api'; +import { useLoadIndexTemplates } from '../../../services/api'; import { Template } from '../../../../../common/types'; import { useServices } from '../../../app_context'; import { @@ -40,7 +40,7 @@ export const TemplateList: React.FunctionComponent { const { uiMetricService } = useServices(); - const { error, isLoading, data: templates, sendRequest: reload } = loadIndexTemplates(); + const { error, isLoading, data: templates, sendRequest: reload } = useLoadIndexTemplates(); let content; @@ -68,7 +68,7 @@ export const TemplateList: React.FunctionComponent { uiMetricService.trackMetric('loaded', UIM_TEMPLATE_LIST_LOAD); - }, []); + }, [uiMetricService]); if (isLoading) { content = ( diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts b/x-pack/plugins/index_management/public/application/sections/template_clone/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts rename to x-pack/plugins/index_management/public/application/sections/template_clone/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx rename to x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx index 6659be5a2cf4e3..cf6ca3c0657773 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx +++ b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx @@ -11,7 +11,7 @@ import { TemplateForm, SectionLoading, SectionError, Error } from '../../compone import { breadcrumbService } from '../../services/breadcrumbs'; import { decodePath, getTemplateDetailsLink } from '../../services/routing'; import { Template } from '../../../../common/types'; -import { saveTemplate, loadIndexTemplate } from '../../services/api'; +import { saveTemplate, useLoadIndexTemplate } from '../../services/api'; interface MatchParams { name: string; @@ -27,7 +27,7 @@ export const TemplateClone: React.FunctionComponent(false); const [saveError, setSaveError] = useState(null); - const { error: templateToCloneError, data: templateToClone, isLoading } = loadIndexTemplate( + const { error: templateToCloneError, data: templateToClone, isLoading } = useLoadIndexTemplate( decodedTemplateName ); diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts b/x-pack/plugins/index_management/public/application/sections/template_create/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts rename to x-pack/plugins/index_management/public/application/sections/template_create/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx b/x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx rename to x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts b/x-pack/plugins/index_management/public/application/sections/template_edit/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts rename to x-pack/plugins/index_management/public/application/sections/template_edit/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx rename to x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx index 69e446528a68d5..1e9d5f294de34c 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx +++ b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx @@ -8,7 +8,7 @@ import { RouteComponentProps } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPageBody, EuiPageContent, EuiTitle, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { breadcrumbService } from '../../services/breadcrumbs'; -import { loadIndexTemplate, updateTemplate } from '../../services/api'; +import { useLoadIndexTemplate, updateTemplate } from '../../services/api'; import { decodePath, getTemplateDetailsLink } from '../../services/routing'; import { SectionLoading, SectionError, TemplateForm, Error } from '../../components'; import { Template } from '../../../../common/types'; @@ -27,7 +27,7 @@ export const TemplateEdit: React.FunctionComponent(false); const [saveError, setSaveError] = useState(null); - const { error, data: template, isLoading } = loadIndexTemplate(decodedTemplateName); + const { error, data: template, isLoading } = useLoadIndexTemplate(decodedTemplateName); useEffect(() => { breadcrumbService.setBreadcrumbs('templateEdit'); diff --git a/x-pack/legacy/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/services/api.ts rename to x-pack/plugins/index_management/public/application/services/api.ts index 642fd441b353a1..88887f40972e43 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/api.ts +++ b/x-pack/plugins/index_management/public/application/services/api.ts @@ -200,7 +200,7 @@ export async function loadIndexData(type: string, indexName: string) { } } -export function loadIndexTemplates() { +export function useLoadIndexTemplates() { return useRequest({ path: `${API_BASE_PATH}/templates`, method: 'get', @@ -220,7 +220,7 @@ export async function deleteTemplates(names: Array) { return result; } -export function loadIndexTemplate(name: Template['name']) { +export function useLoadIndexTemplate(name: Template['name']) { return useRequest({ path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`, method: 'get', diff --git a/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts b/x-pack/plugins/index_management/public/application/services/breadcrumbs.ts similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts rename to x-pack/plugins/index_management/public/application/services/breadcrumbs.ts index 299491756560ee..8128ccd545dceb 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts +++ b/x-pack/plugins/index_management/public/application/services/breadcrumbs.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; import { BASE_PATH } from '../../../common/constants'; -import { ManagementAppMountParams } from '../../../../../../../src/plugins/management/public'; +import { ManagementAppMountParams } from '../../../../../../src/plugins/management/public'; type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs']; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts b/x-pack/plugins/index_management/public/application/services/documentation.ts similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/services/documentation.ts rename to x-pack/plugins/index_management/public/application/services/documentation.ts index e0f261e586b1e7..fdf07c43a0c8b2 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts +++ b/x-pack/plugins/index_management/public/application/services/documentation.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DocLinksStart } from '../../../../../../../src/core/public'; +import { DocLinksStart } from '../../../../../../src/core/public'; import { DataType } from '../components/mappings_editor/types'; import { TYPE_DEFINITION } from '../components/mappings_editor/constants'; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts b/x-pack/plugins/index_management/public/application/services/health_to_color.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts rename to x-pack/plugins/index_management/public/application/services/health_to_color.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/http.ts b/x-pack/plugins/index_management/public/application/services/http.ts similarity index 88% rename from x-pack/legacy/plugins/index_management/public/application/services/http.ts rename to x-pack/plugins/index_management/public/application/services/http.ts index a6973c263f00f2..931e5fcd21898f 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/http.ts +++ b/x-pack/plugins/index_management/public/application/services/http.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from '../../../../../../../src/core/public'; +import { HttpSetup } from '../../../../../../src/core/public'; export class HttpService { private client: any; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/index.ts b/x-pack/plugins/index_management/public/application/services/index.ts similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/services/index.ts rename to x-pack/plugins/index_management/public/application/services/index.ts index 78ff8cb5c314ac..2334d32adf1314 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/index.ts +++ b/x-pack/plugins/index_management/public/application/services/index.ts @@ -21,7 +21,7 @@ export { loadIndexStats, loadIndexMapping, loadIndexData, - loadIndexTemplates, + useLoadIndexTemplates, } from './api'; export { healthToColor } from './health_to_color'; export { sortTable } from './sort_table'; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/navigation.ts b/x-pack/plugins/index_management/public/application/services/navigation.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/navigation.ts rename to x-pack/plugins/index_management/public/application/services/navigation.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/notification.ts b/x-pack/plugins/index_management/public/application/services/notification.ts similarity index 93% rename from x-pack/legacy/plugins/index_management/public/application/services/notification.ts rename to x-pack/plugins/index_management/public/application/services/notification.ts index 0971ca77c004bb..82b9de22727471 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/notification.ts +++ b/x-pack/plugins/index_management/public/application/services/notification.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { NotificationsStart } from '../../../../../../../src/core/public'; +import { NotificationsStart } from '../../../../../../src/core/public'; export class NotificationService { private _toasts: any; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/routing.ts b/x-pack/plugins/index_management/public/application/services/routing.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/routing.ts rename to x-pack/plugins/index_management/public/application/services/routing.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts b/x-pack/plugins/index_management/public/application/services/sort_table.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts rename to x-pack/plugins/index_management/public/application/services/sort_table.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts b/x-pack/plugins/index_management/public/application/services/ui_metric.ts similarity index 90% rename from x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts rename to x-pack/plugins/index_management/public/application/services/ui_metric.ts index 7c87ec9509085c..73d2ef5aced861 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts +++ b/x-pack/plugins/index_management/public/application/services/ui_metric.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { UiStatsMetricType } from '@kbn/analytics'; -import { UsageCollectionSetup } from '../../../../../../../src/plugins/usage_collection/public'; + +import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/public'; import { IndexMgmtMetricsType } from '../../types'; let uiMetricService: UiMetricService; @@ -23,7 +24,8 @@ export class UiMetricService { private track(type: T, name: string) { if (!this.usageCollection) { - throw Error('UiMetricService not initialized.'); + // Usage collection might have been disabled in Kibana config. + return; } this.usageCollection.reportUiStats(this.appName, type as UiStatsMetricType, name); } diff --git a/x-pack/legacy/plugins/index_management/public/application/services/use_request.ts b/x-pack/plugins/index_management/public/application/services/use_request.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/use_request.ts rename to x-pack/plugins/index_management/public/application/services/use_request.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js b/x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js b/x-pack/plugins/index_management/public/application/store/actions/clear_row_status.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js rename to x-pack/plugins/index_management/public/application/store/actions/clear_row_status.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js b/x-pack/plugins/index_management/public/application/store/actions/close_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/close_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js b/x-pack/plugins/index_management/public/application/store/actions/delete_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/delete_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js b/x-pack/plugins/index_management/public/application/store/actions/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js rename to x-pack/plugins/index_management/public/application/store/actions/detail_panel.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js b/x-pack/plugins/index_management/public/application/store/actions/edit_index_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js rename to x-pack/plugins/index_management/public/application/store/actions/edit_index_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js b/x-pack/plugins/index_management/public/application/store/actions/extension_action.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js rename to x-pack/plugins/index_management/public/application/store/actions/extension_action.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js b/x-pack/plugins/index_management/public/application/store/actions/flush_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/flush_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js b/x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js b/x-pack/plugins/index_management/public/application/store/actions/freeze_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/freeze_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/index.js b/x-pack/plugins/index_management/public/application/store/actions/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/index.js rename to x-pack/plugins/index_management/public/application/store/actions/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js b/x-pack/plugins/index_management/public/application/store/actions/load_index_data.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js rename to x-pack/plugins/index_management/public/application/store/actions/load_index_data.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js b/x-pack/plugins/index_management/public/application/store/actions/load_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/load_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js b/x-pack/plugins/index_management/public/application/store/actions/open_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/open_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js b/x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js b/x-pack/plugins/index_management/public/application/store/actions/reload_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/reload_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js b/x-pack/plugins/index_management/public/application/store/actions/table_state.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js rename to x-pack/plugins/index_management/public/application/store/actions/table_state.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js b/x-pack/plugins/index_management/public/application/store/actions/unfreeze_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/unfreeze_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js b/x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js rename to x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/index.ts b/x-pack/plugins/index_management/public/application/store/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/index.ts rename to x-pack/plugins/index_management/public/application/store/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js b/x-pack/plugins/index_management/public/application/store/reducers/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js rename to x-pack/plugins/index_management/public/application/store/reducers/detail_panel.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js b/x-pack/plugins/index_management/public/application/store/reducers/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js rename to x-pack/plugins/index_management/public/application/store/reducers/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js b/x-pack/plugins/index_management/public/application/store/reducers/index_management.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js rename to x-pack/plugins/index_management/public/application/store/reducers/index_management.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js b/x-pack/plugins/index_management/public/application/store/reducers/indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js rename to x-pack/plugins/index_management/public/application/store/reducers/indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js b/x-pack/plugins/index_management/public/application/store/reducers/row_status.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js rename to x-pack/plugins/index_management/public/application/store/reducers/row_status.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js b/x-pack/plugins/index_management/public/application/store/reducers/table_state.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js rename to x-pack/plugins/index_management/public/application/store/reducers/table_state.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts b/x-pack/plugins/index_management/public/application/store/selectors/index.d.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts rename to x-pack/plugins/index_management/public/application/store/selectors/index.d.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js rename to x-pack/plugins/index_management/public/application/store/selectors/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/store.d.ts b/x-pack/plugins/index_management/public/application/store/store.d.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/store.d.ts rename to x-pack/plugins/index_management/public/application/store/store.d.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/store.js b/x-pack/plugins/index_management/public/application/store/store.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/store.js rename to x-pack/plugins/index_management/public/application/store/store.js diff --git a/x-pack/legacy/plugins/index_management/public/index.scss b/x-pack/plugins/index_management/public/index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/index.scss rename to x-pack/plugins/index_management/public/index.scss diff --git a/x-pack/legacy/plugins/index_management/public/index.ts b/x-pack/plugins/index_management/public/index.ts similarity index 66% rename from x-pack/legacy/plugins/index_management/public/index.ts rename to x-pack/plugins/index_management/public/index.ts index 16e7bf21aee98d..6bb921ef648f34 100644 --- a/x-pack/legacy/plugins/index_management/public/index.ts +++ b/x-pack/plugins/index_management/public/index.ts @@ -3,19 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { npSetup } from 'ui/new_platform'; - +import './index.scss'; import { IndexMgmtUIPlugin, IndexMgmtSetup } from './plugin'; /** @public */ -export { IndexMgmtSetup }; - export const plugin = () => { return new IndexMgmtUIPlugin(); }; -// Temp. To be removed after moving to the "plugins" folder - -const { extensionsService } = plugin().setup(npSetup.core, npSetup.plugins); +export { IndexMgmtSetup }; -export { extensionsService }; +export { getIndexListUri } from './application/services/navigation'; diff --git a/x-pack/legacy/plugins/index_management/public/mocks.ts b/x-pack/plugins/index_management/public/mocks.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/mocks.ts rename to x-pack/plugins/index_management/public/mocks.ts diff --git a/x-pack/legacy/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts similarity index 90% rename from x-pack/legacy/plugins/index_management/public/plugin.ts rename to x-pack/plugins/index_management/public/plugin.ts index 539324766cf955..c1b26fe3ca56b4 100644 --- a/x-pack/legacy/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { CoreSetup } from '../../../../../src/core/public'; -import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public'; -import { ManagementSetup } from '../../../../../src/plugins/management/public'; -import { UIM_APP_NAME } from '../common/constants'; +import { CoreSetup } from '../../../../src/core/public'; +import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; +import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { UIM_APP_NAME, PLUGIN } from '../common/constants'; import { AppDependencies } from './application'; import { httpService } from './application/services/http'; @@ -52,7 +52,7 @@ export class IndexMgmtUIPlugin { this.uiMetricService.setup(usageCollection); management.sections.getSection('elasticsearch')!.registerApp({ - id: 'index_management', + id: PLUGIN.id, title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), order: 1, mount: async ({ element, setBreadcrumbs }) => { diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts b/x-pack/plugins/index_management/public/services/extensions_service.mock.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts rename to x-pack/plugins/index_management/public/services/extensions_service.mock.ts diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.ts b/x-pack/plugins/index_management/public/services/extensions_service.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/services/extensions_service.ts rename to x-pack/plugins/index_management/public/services/extensions_service.ts diff --git a/x-pack/legacy/plugins/index_management/public/services/index.ts b/x-pack/plugins/index_management/public/services/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/services/index.ts rename to x-pack/plugins/index_management/public/services/index.ts diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts new file mode 100644 index 00000000000000..cd2964df23d9bf --- /dev/null +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -0,0 +1,33 @@ +/* + * 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 { + SendRequestConfig, + SendRequestResponse, + UseRequestConfig, + sendRequest, + useRequest, +} from '../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; + +export { + FormSchema, + FIELD_TYPES, + VALIDATION_TYPES, + FieldConfig, + useForm, + Form, + getUseField, +} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; + +export { + fieldFormatters, + fieldValidators, + serializers, +} from '../../../../src/plugins/es_ui_shared/static/forms/helpers'; + +export { getFormRow, Field } from '../../../../src/plugins/es_ui_shared/static/forms/components'; + +export { isJSON } from '../../../../src/plugins/es_ui_shared/static/validators/string'; diff --git a/x-pack/legacy/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/types.ts rename to x-pack/plugins/index_management/public/types.ts diff --git a/x-pack/legacy/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/server/config.ts similarity index 52% rename from x-pack/legacy/plugins/index_management/public/shared_imports.ts rename to x-pack/plugins/index_management/server/config.ts index cbc4dde7448ff0..5f03575d3ff436 100644 --- a/x-pack/legacy/plugins/index_management/public/shared_imports.ts +++ b/x-pack/plugins/index_management/server/config.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - SendRequestConfig, - SendRequestResponse, - UseRequestConfig, - sendRequest, - useRequest, -} from '../../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; +import { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), +}); + +export type IndexManagementConfig = TypeOf; diff --git a/x-pack/legacy/plugins/index_management/server/index.ts b/x-pack/plugins/index_management/server/index.ts similarity index 67% rename from x-pack/legacy/plugins/index_management/server/index.ts rename to x-pack/plugins/index_management/server/index.ts index 866b374740d3b2..e4102711708cbb 100644 --- a/x-pack/legacy/plugins/index_management/server/index.ts +++ b/x-pack/plugins/index_management/server/index.ts @@ -5,8 +5,18 @@ */ import { PluginInitializerContext } from 'src/core/server'; + import { IndexMgmtServerPlugin } from './plugin'; +import { configSchema } from './config'; export const plugin = (ctx: PluginInitializerContext) => new IndexMgmtServerPlugin(ctx); +export const config = { + schema: configSchema, +}; + +/** @public */ export { Dependencies } from './types'; +export { IndexMgmtSetup } from './plugin'; +export { Index } from './types'; +export { IndexManagementConfig } from './config'; diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.test.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.test.ts rename to x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.ts rename to x-pack/plugins/index_management/server/lib/fetch_aliases.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/fetch_indices.ts rename to x-pack/plugins/index_management/server/lib/fetch_indices.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/get_managed_templates.ts b/x-pack/plugins/index_management/server/lib/get_managed_templates.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/get_managed_templates.ts rename to x-pack/plugins/index_management/server/lib/get_managed_templates.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/is_es_error.ts b/x-pack/plugins/index_management/server/lib/is_es_error.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/is_es_error.ts rename to x-pack/plugins/index_management/server/lib/is_es_error.ts diff --git a/x-pack/legacy/plugins/index_management/server/plugin.ts b/x-pack/plugins/index_management/server/plugin.ts similarity index 94% rename from x-pack/legacy/plugins/index_management/server/plugin.ts rename to x-pack/plugins/index_management/server/plugin.ts index 95d27e1cf16bae..a0a9151cdb71f5 100644 --- a/x-pack/legacy/plugins/index_management/server/plugin.ts +++ b/x-pack/plugins/index_management/server/plugin.ts @@ -24,8 +24,8 @@ export class IndexMgmtServerPlugin implements Plugin Date: Tue, 25 Feb 2020 09:05:22 +0100 Subject: [PATCH 20/21] [SIEM] Upgrades cypress to version 4.0.2 (#58400) * upgrades cypress to version 4.0.2 * fixes failing tests --- .../timeline_search_or_filter.spec.ts | 5 +- .../cypress/integration/url_state.spec.ts | 5 +- .../cypress/tasks/hosts/authentications.ts | 4 + .../cypress/tasks/hosts/uncommon_processes.ts | 4 + .../plugins/siem/cypress/tasks/timeline.ts | 5 +- .../components/fields_browser/header.test.tsx | 2 - x-pack/package.json | 2 +- yarn.lock | 301 ++++++++---------- 8 files changed, 159 insertions(+), 169 deletions(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts index c06fd69a558a40..f738ff792049ad 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts @@ -24,6 +24,9 @@ describe('timeline search or filter KQL bar', () => { cy.get(SERVER_SIDE_EVENT_COUNT) .invoke('text') - .should('be.above', 0); + .then(strCount => { + const intCount = +strCount; + cy.wrap(intCount).should('be.above', 0); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts index cabdb98fa5b676..11c0562eb3638d 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts @@ -236,7 +236,10 @@ describe('url state', () => { cy.get(SERVER_SIDE_EVENT_COUNT) .invoke('text') - .should('be.above', 0); + .then(strCount => { + const intCount = +strCount; + cy.wrap(intCount).should('be.above', 0); + }); const bestTimelineName = 'The Best Timeline'; addNameToTimeline(bestTimelineName); diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts index f5f15150e8ac31..ce3767a3403762 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts @@ -5,7 +5,11 @@ */ import { AUTHENTICATIONS_TABLE } from '../../screens/hosts/authentications'; +import { REFRESH_BUTTON } from '../../screens/siem_header'; export const waitForAuthenticationsToBeLoaded = () => { cy.get(AUTHENTICATIONS_TABLE).should('exist'); + cy.get(REFRESH_BUTTON) + .invoke('text') + .should('not.equal', 'Updating'); }; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts index c44249acdd9649..a28a7df07c3f82 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts @@ -5,7 +5,11 @@ */ import { UNCOMMON_PROCESSES_TABLE } from '../../screens/hosts/uncommon_processes'; +import { REFRESH_BUTTON } from '../../screens/siem_header'; export const waitForUncommonProcessesToBeLoaded = () => { cy.get(UNCOMMON_PROCESSES_TABLE).should('exist'); + cy.get(REFRESH_BUTTON) + .invoke('text') + .should('not.equal', 'Updating'); }; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts index 76acdad990a7e0..c218d5153356b6 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts @@ -65,7 +65,10 @@ export const populateTimeline = () => { executeTimelineKQL(hostExistsQuery); cy.get(SERVER_SIDE_EVENT_COUNT) .invoke('text') - .should('be.above', 0); + .then(strCount => { + const intCount = +strCount; + cy.wrap(intCount).should('be.above', 0); + }); }; export const uncheckTimestampToggleField = () => { diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx index 42689065354d00..2abc2fd1046e06 100644 --- a/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx @@ -6,11 +6,9 @@ import { mount } from 'enzyme'; import React from 'react'; - import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { defaultHeaders } from '../timeline/body/column_headers/default_headers'; - import { Header } from './header'; const timelineId = 'test'; diff --git a/x-pack/package.json b/x-pack/package.json index 551e466893f931..f76b0182ea228d 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -117,7 +117,7 @@ "cheerio": "0.22.0", "commander": "3.0.2", "copy-webpack-plugin": "^5.0.4", - "cypress": "^3.6.1", + "cypress": "^4.0.2", "cypress-multi-reporters": "^1.2.3", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", diff --git a/yarn.lock b/yarn.lock index 8ea23c17b8b8b2..f46e869909e2e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7023,13 +7023,6 @@ async@2.4.0: dependencies: lodash "^4.14.0" -async@2.6.1, async@^2.6.0, async@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== - dependencies: - lodash "^4.17.10" - async@^2.0.0, async@^2.1.4: version "2.6.0" resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" @@ -7037,6 +7030,13 @@ async@^2.0.0, async@^2.1.4: dependencies: lodash "^4.14.0" +async@^2.6.0, async@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== + dependencies: + lodash "^4.17.10" + async@^2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -7044,6 +7044,11 @@ async@^2.6.3: dependencies: lodash "^4.17.14" +async@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + async@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" @@ -7909,6 +7914,11 @@ bluebird@3.5.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== +bluebird@3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + bluebird@^3.3.0, bluebird@^3.3.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -8536,12 +8546,10 @@ cacheable-request@^2.1.1: normalize-url "2.0.1" responselike "1.0.2" -cachedir@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.3.0.tgz#5e01928bf2d95b5edd94b0942188246740e0dbc4" - integrity sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg== - dependencies: - os-homedir "^1.0.1" +cachedir@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== caching-transform@^3.0.2: version "3.0.2" @@ -8806,6 +8814,14 @@ chalk@2.4.2, chalk@^2.3.2, chalk@^2.4.2, chalk@~2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@3.0.0, chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -8835,14 +8851,6 @@ chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^5.2.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" @@ -9087,11 +9095,6 @@ ci-info@^1.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.2.tgz#03561259db48d0474c8bdc90f5b47b068b6bbfb4" integrity sha512-uTGIPNx/nSpBdsF6xnseRXLLtfr9VLqkz8ZqHXr3Y7b6SftyRxBGjwMtJj1OhNbmlc1wZzLNAlAcvyIiE8a6ZA== -ci-info@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -9596,11 +9599,6 @@ commander@2, commander@2.19.0, commander@^2.11.0, commander@^2.12.2: resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== - commander@2.17.x, commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" @@ -9611,6 +9609,11 @@ commander@3.0.2: resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== +commander@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83" + integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw== + commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" @@ -10646,41 +10649,42 @@ cypress-multi-reporters@^1.2.3: debug "^4.1.1" lodash "^4.17.11" -cypress@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.6.1.tgz#4420957923879f60b7a5146ccbf81841a149b653" - integrity sha512-6n0oqENdz/oQ7EJ6IgESNb2M7Bo/70qX9jSJsAziJTC3kICfEMmJUlrAnP9bn+ut24MlXQST5nRXhUP5nRIx6A== +cypress@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.0.2.tgz#ede194d7bc73fb449f8de553c9e1db4ca15309ef" + integrity sha512-WRzxOoSd+TxyXKa7Zi9orz3ii5VW7yhhVYstCU+EpOKfPan9x5Ww2Clucmy4H/W0GHUYAo7GYFZRD33ZCSNBQA== dependencies: "@cypress/listr-verbose-renderer" "0.4.1" "@cypress/xvfb" "1.2.4" "@types/sizzle" "2.3.2" arch "2.1.1" - bluebird "3.5.0" - cachedir "1.3.0" - chalk "2.4.2" + bluebird "3.7.2" + cachedir "2.3.0" + chalk "3.0.0" check-more-types "2.24.0" - commander "2.15.1" + commander "4.1.0" common-tags "1.8.0" - debug "3.2.6" - execa "0.10.0" + debug "4.1.1" + eventemitter2 "4.1.2" + execa "3.3.0" executable "4.1.1" extract-zip "1.6.7" - fs-extra "5.0.0" - getos "3.1.1" - is-ci "1.2.1" + fs-extra "8.1.0" + getos "3.1.4" + is-ci "2.0.0" is-installed-globally "0.1.0" lazy-ass "1.6.0" - listr "0.12.0" + listr "0.14.3" lodash "4.17.15" - log-symbols "2.2.0" + log-symbols "3.0.0" minimist "1.2.0" moment "2.24.0" - ramda "0.24.1" + ramda "0.26.1" request "2.88.0" request-progress "3.0.0" - supports-color "5.5.0" + supports-color "7.1.0" tmp "0.1.0" - untildify "3.0.3" + untildify "4.0.0" url "0.11.0" yauzl "2.10.0" @@ -11065,7 +11069,7 @@ debug@4.1.0: dependencies: ms "^2.1.1" -debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4.1.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -13028,6 +13032,11 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventemitter2@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15" + integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU= + eventemitter2@~0.4.13: version "0.4.14" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" @@ -13078,19 +13087,6 @@ exec-sh@^0.3.2: resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg== -execa@0.10.0, execa@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@1.0.0, execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -13104,6 +13100,22 @@ execa@1.0.0, execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.3.0.tgz#7e348eef129a1937f21ecbbd53390942653522c1" + integrity sha512-j5Vit5WZR/cbHlqU97+qcnw9WHRCIL4V1SVe75VcHcD1JRBdt8fv0zw89b7CQHQdUHTt2VjuhcF5ibAgVOxqpg== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + execa@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-0.1.1.tgz#b09c2a9309bc0ef0501479472db3180f8d4c3edd" @@ -13113,6 +13125,19 @@ execa@^0.1.1: object-assign "^4.0.1" strip-eof "^1.0.0" +execa@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.4.0.tgz#4eb6467a36a095fabb2970ff9d5e3fb7bce6ebc3" @@ -14321,12 +14346,12 @@ fs-exists-sync@^0.1.0: resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= -fs-extra@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== +fs-extra@8.1.0, fs-extra@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: - graceful-fs "^4.1.2" + graceful-fs "^4.2.0" jsonfile "^4.0.0" universalify "^0.1.0" @@ -14368,15 +14393,6 @@ fs-extra@^7.0.0, fs-extra@^7.0.1, fs-extra@~7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -14703,12 +14719,12 @@ getopts@^2.2.5: resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b" integrity sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA== -getos@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.1.tgz#967a813cceafee0156b0483f7cffa5b3eff029c5" - integrity sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg== +getos@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.4.tgz#29cdf240ed10a70c049add7b6f8cb08c81876faf" + integrity sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw== dependencies: - async "2.6.1" + async "^3.1.0" getos@^3.1.0: version "3.1.0" @@ -17152,12 +17168,12 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== -is-ci@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" - integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== +is-ci@2.0.0, is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: - ci-info "^1.5.0" + ci-info "^2.0.0" is-ci@^1.0.10: version "1.1.0" @@ -17166,13 +17182,6 @@ is-ci@^1.0.10: dependencies: ci-info "^1.0.0" -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -19254,20 +19263,6 @@ listr-update-renderer@0.5.0, listr-update-renderer@^0.5.0: log-update "^2.3.0" strip-ansi "^3.0.1" -listr-update-renderer@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9" - integrity sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - listr-update-renderer@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" @@ -19302,28 +19297,6 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" -listr@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" - integrity sha1-a84sD1YD+klYDqF81qAMwOX6RRo= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.2.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.0.0-beta.11" - stream-to-observable "^0.1.0" - strip-ansi "^3.0.1" - listr@0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" @@ -19824,6 +19797,13 @@ log-symbols@2.2.0, log-symbols@^2.0.0, log-symbols@^2.1.0, log-symbols@^2.2.0: dependencies: chalk "^2.0.1" +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + log-symbols@^1.0.1, log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -23983,21 +23963,16 @@ railroad-diagrams@^1.0.0: resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= -ramda@0.24.1: - version "0.24.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" - integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= +ramda@0.26.1, ramda@^0.26, ramda@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== ramda@^0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= -ramda@^0.26, ramda@^0.26.1: - version "0.26.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" - integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== - randexp@0.4.6: version "0.4.6" resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" @@ -26375,7 +26350,7 @@ rxjs@6.5.2: dependencies: tslib "^1.9.0" -rxjs@^5.0.0-beta.11, rxjs@^5.5.2: +rxjs@^5.5.2: version "5.5.12" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== @@ -27815,11 +27790,6 @@ stream-spigot@~2.1.2: dependencies: readable-stream "~1.1.0" -stream-to-observable@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" - integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4= - streamroller@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b" @@ -28291,13 +28261,6 @@ supertest@^3.1.0: methods "~1.1.2" superagent "3.8.2" -supports-color@5.5.0, supports-color@^5.0.0, supports-color@^5.4.0, supports-color@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - supports-color@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" @@ -28312,6 +28275,13 @@ supports-color@6.1.0, supports-color@^6.0.0, supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +supports-color@7.1.0, supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + supports-color@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" @@ -28329,6 +28299,13 @@ supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3: dependencies: has-flag "^1.0.0" +supports-color@^5.0.0, supports-color@^5.4.0, supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + supports-color@^5.2.0, supports-color@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" @@ -28343,13 +28320,6 @@ supports-color@^7.0.0: dependencies: has-flag "^4.0.0" -supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== - dependencies: - has-flag "^4.0.0" - supports-hyperlinks@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7" @@ -30241,10 +30211,10 @@ unstated@^2.1.1: dependencies: create-react-context "^0.1.5" -untildify@3.0.3, untildify@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9" - integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA== +untildify@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== untildify@^2.0.0: version "2.1.0" @@ -30253,6 +30223,11 @@ untildify@^2.0.0: dependencies: os-homedir "^1.0.0" +untildify@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9" + integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA== + unzip-response@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" From a60b25f9ad45cbc8efb5f362d30996c0d49baa2b Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 25 Feb 2020 09:05:53 +0100 Subject: [PATCH 21/21] fix short url in spaces (#58313) --- src/plugins/share/server/routes/goto.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/share/server/routes/goto.ts b/src/plugins/share/server/routes/goto.ts index 5c3a4e441099fb..0c5b74915e58a6 100644 --- a/src/plugins/share/server/routes/goto.ts +++ b/src/plugins/share/server/routes/goto.ts @@ -23,6 +23,7 @@ import { schema } from '@kbn/config-schema'; import { shortUrlAssertValid } from './lib/short_url_assert_valid'; import { ShortUrlLookupService } from './lib/short_url_lookup'; import { getGotoPath } from '../../common/short_url_routes'; +import { modifyUrl } from '../../../../core/utils'; export const createGotoRoute = ({ router, @@ -49,9 +50,16 @@ export const createGotoRoute = ({ const uiSettings = context.core.uiSettings.client; const stateStoreInSessionStorage = await uiSettings.get('state:storeInSessionStorage'); if (!stateStoreInSessionStorage) { + const basePath = http.basePath.get(request); + + const prependedUrl = modifyUrl(url, parts => { + if (!parts.hostname && parts.pathname && parts.pathname.startsWith('/')) { + parts.pathname = `${basePath}${parts.pathname}`; + } + }); return response.redirected({ headers: { - location: http.basePath.prepend(url), + location: prependedUrl, }, }); }