From 30cc81fc0d311b7b55fcd9746a7b43d360881b35 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 20 Feb 2020 12:09:04 -0500 Subject: [PATCH 01/19] Fix useRequest to support query change (#57723) (#58067) --- .../public/request/np_ready_request.ts | 25 +++++++++++++------ .../public/request/request.test.js | 10 ++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/plugins/es_ui_shared/public/request/np_ready_request.ts b/src/plugins/es_ui_shared/public/request/np_ready_request.ts index 790e29b6d36553..01144fa1da2e98 100644 --- a/src/plugins/es_ui_shared/public/request/np_ready_request.ts +++ b/src/plugins/es_ui_shared/public/request/np_ready_request.ts @@ -17,7 +17,7 @@ * under the License. */ -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useState, useRef, useMemo } from 'react'; import { HttpSetup, HttpFetchQuery } from '../../../../../src/core/public'; @@ -78,6 +78,8 @@ export const useRequest = ( deserializer = (data: any): any => data, }: UseRequestConfig ): UseRequestResponse => { + const sendRequestRef = useRef<() => Promise>(); + // Main states for tracking request status and data const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); @@ -102,7 +104,10 @@ export const useRequest = ( // Set new interval if (pollInterval.current) { - pollIntervalId.current = setTimeout(_sendRequest, pollInterval.current); + pollIntervalId.current = setTimeout( + () => (sendRequestRef.current ?? _sendRequest)(), + pollInterval.current + ); } }; @@ -145,11 +150,17 @@ export const useRequest = ( }; useEffect(() => { - _sendRequest(); - // To be functionally correct we'd send a new request if the method, path, or body changes. + sendRequestRef.current = _sendRequest; + }, [_sendRequest]); + + const stringifiedQuery = useMemo(() => JSON.stringify(query), [query]); + + useEffect(() => { + (sendRequestRef.current ?? _sendRequest)(); + // To be functionally correct we'd send a new request if the method, path, query or body changes. // But it doesn't seem likely that the method will change and body is likely to be a new - // object even if its shape hasn't changed, so for now we're just watching the path. - }, [path]); + // object even if its shape hasn't changed, so for now we're just watching the path and the query. + }, [path, stringifiedQuery]); useEffect(() => { scheduleRequest(); @@ -168,6 +179,6 @@ export const useRequest = ( isLoading, error, data, - sendRequest: _sendRequest, // Gives the user the ability to manually request data + sendRequest: sendRequestRef.current ?? _sendRequest, // Gives the user the ability to manually request data }; }; diff --git a/src/plugins/es_ui_shared/public/request/request.test.js b/src/plugins/es_ui_shared/public/request/request.test.js index 8c7e2b6ddc72ad..44bf149d5fd1e9 100644 --- a/src/plugins/es_ui_shared/public/request/request.test.js +++ b/src/plugins/es_ui_shared/public/request/request.test.js @@ -71,7 +71,7 @@ describe.skip('request lib', () => { it('uses the provided path, method, and body to send the request', async () => { const response = await sendRequest({ ...successRequest }); sinon.assert.calledOnce(sendPost); - expect(response).toEqual({ data: successResponse.data }); + expect(response).toEqual({ data: successResponse.data, error: null }); }); it('surfaces errors', async () => { @@ -182,11 +182,11 @@ describe.skip('request lib', () => { expect(hook.error).toBe(errorResponse); }); - it('is undefined when the request is successful', async () => { + it('is null when the request is successful', async () => { initUseRequest({ ...successRequest }); await wait(50); expect(hook.isLoading).toBe(false); - expect(hook.error).toBeUndefined(); + expect(hook.error).toBeNull(); }); }); @@ -205,11 +205,11 @@ describe.skip('request lib', () => { expect(hook.data).toBe(successResponse.data); }); - it('is undefined when the request fails', async () => { + it('is null when the request fails', async () => { initUseRequest({ ...errorRequest }); await wait(50); expect(hook.isLoading).toBe(false); - expect(hook.data).toBeUndefined(); + expect(hook.data).toBeNull(); }); }); }); From e31ce900b4a33bdfed36b526c5d1eae5d812c2cc Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Thu, 20 Feb 2020 18:51:57 +0100 Subject: [PATCH 02/19] [ML] New Platform server shim: update system routes (#57835) (#58011) * [ML] NP system routes * [ML] apidoc.json * [ML] address PR comments * [ML] fix apidoc methods, passing es_search endpoint payload * [ML] add dummy body validation for es_search, fix ignoreSpaces query param * [ML] _has_privileges validate body --- .../lib/check_privileges/check_privileges.ts | 8 +- .../ml/server/lib/check_privileges/upgrade.ts | 6 +- .../legacy/plugins/ml/server/routes/system.js | 205 --------------- .../legacy/plugins/ml/server/routes/system.ts | 247 ++++++++++++++++++ 4 files changed, 254 insertions(+), 212 deletions(-) delete mode 100644 x-pack/legacy/plugins/ml/server/routes/system.js create mode 100644 x-pack/legacy/plugins/ml/server/routes/system.ts diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts index 6b426169799a7b..617778afbe121b 100644 --- a/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { IScopedClusterClient } from 'kibana/server'; import { Privileges, getDefaultPrivileges } from '../../../common/types/privileges'; import { XPackMainPlugin } from '../../../../xpack_main/server/xpack_main'; -import { callWithRequestType } from '../../../common/types/kibana'; import { isSecurityDisabled } from '../../lib/security_utils'; import { upgradeCheckProvider } from './upgrade'; import { checkLicense } from '../check_license'; @@ -24,12 +24,12 @@ interface Response { } export function privilegesProvider( - callWithRequest: callWithRequestType, + callAsCurrentUser: IScopedClusterClient['callAsCurrentUser'], xpackMainPlugin: XPackMainPlugin, isMlEnabledInSpace: () => Promise, ignoreSpaces: boolean = false ) { - const { isUpgradeInProgress } = upgradeCheckProvider(callWithRequest); + const { isUpgradeInProgress } = upgradeCheckProvider(callAsCurrentUser); async function getPrivileges(): Promise { // get the default privileges, forced to be false. const privileges = getDefaultPrivileges(); @@ -74,7 +74,7 @@ export function privilegesProvider( } else { // security enabled // load all ml privileges for this user. - const { cluster } = await callWithRequest('ml.privilegeCheck', { body: mlPrivileges }); + const { cluster } = await callAsCurrentUser('ml.privilegeCheck', { body: mlPrivileges }); setGettingPrivileges(cluster, privileges); if (upgradeInProgress === false) { // if an upgrade is in progress, don't apply the "setting" diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts index 9e62780c51b3e8..a1d66f00f26e19 100644 --- a/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { IScopedClusterClient } from 'kibana/server'; import { mlLog } from '../../client/log'; -import { callWithRequestType } from '../../../common/types/kibana'; -export function upgradeCheckProvider(callWithRequest: callWithRequestType) { +export function upgradeCheckProvider(callAsCurrentUser: IScopedClusterClient['callAsCurrentUser']) { async function isUpgradeInProgress(): Promise { let upgradeInProgress = false; try { - const info = await callWithRequest('ml.info'); + const info = await callAsCurrentUser('ml.info'); // if ml indices are currently being migrated, upgrade_mode will be set to true // pass this back with the privileges to allow for the disabling of UI controls. upgradeInProgress = info.upgrade_mode === true; diff --git a/x-pack/legacy/plugins/ml/server/routes/system.js b/x-pack/legacy/plugins/ml/server/routes/system.js deleted file mode 100644 index fd4f3f9b61917b..00000000000000 --- a/x-pack/legacy/plugins/ml/server/routes/system.js +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { callWithRequestFactory } from '../client/call_with_request_factory'; -import { callWithInternalUserFactory } from '../client/call_with_internal_user_factory'; -import { privilegesProvider } from '../lib/check_privileges'; -import { spacesUtilsProvider } from '../lib/spaces_utils'; - -import { mlLog } from '../client/log'; - -import { wrapError } from '../client/errors'; -import Boom from 'boom'; - -import { isSecurityDisabled } from '../lib/security_utils'; - -export function systemRoutes({ - commonRouteConfig, - elasticsearchPlugin, - route, - xpackMainPlugin, - spacesPlugin, - cloud, -}) { - const callWithInternalUser = callWithInternalUserFactory(elasticsearchPlugin); - - function getNodeCount() { - const filterPath = 'nodes.*.attributes'; - return callWithInternalUser('nodes.info', { filterPath }).then(resp => { - let count = 0; - if (typeof resp.nodes === 'object') { - Object.keys(resp.nodes).forEach(k => { - if (resp.nodes[k].attributes !== undefined) { - const maxOpenJobs = resp.nodes[k].attributes['ml.max_open_jobs']; - if (maxOpenJobs !== null && maxOpenJobs > 0) { - count++; - } - } - }); - } - return { count }; - }); - } - - route({ - method: 'POST', - path: '/api/ml/_has_privileges', - async handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - try { - let upgradeInProgress = false; - try { - const info = await callWithRequest('ml.info'); - // if ml indices are currently being migrated, upgrade_mode will be set to true - // pass this back with the privileges to allow for the disabling of UI controls. - upgradeInProgress = info.upgrade_mode === true; - } catch (error) { - // if the ml.info check fails, it could be due to the user having insufficient privileges - // most likely they do not have the ml_user role and therefore will be blocked from using - // ML at all. However, we need to catch this error so the privilege check doesn't fail. - if (error.status === 403) { - mlLog.info( - 'Unable to determine whether upgrade is being performed due to insufficient user privileges' - ); - } else { - mlLog.warn('Unable to determine whether upgrade is being performed'); - } - } - - if (isSecurityDisabled(xpackMainPlugin)) { - // if xpack.security.enabled has been explicitly set to false - // return that security is disabled and don't call the privilegeCheck endpoint - return { - securityDisabled: true, - upgradeInProgress, - }; - } else { - const body = request.payload; - const resp = await callWithRequest('ml.privilegeCheck', { body }); - resp.upgradeInProgress = upgradeInProgress; - return resp; - } - } catch (error) { - return wrapError(error); - } - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'GET', - path: '/api/ml/ml_capabilities', - async handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - try { - const ignoreSpaces = request.query && request.query.ignoreSpaces === 'true'; - // if spaces is disabled force isMlEnabledInSpace to be true - const { isMlEnabledInSpace } = - spacesPlugin !== undefined - ? spacesUtilsProvider(spacesPlugin, request) - : { isMlEnabledInSpace: async () => true }; - - const { getPrivileges } = privilegesProvider( - callWithRequest, - xpackMainPlugin, - isMlEnabledInSpace, - ignoreSpaces - ); - return await getPrivileges(); - } catch (error) { - return wrapError(error); - } - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'GET', - path: '/api/ml/ml_node_count', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - return new Promise((resolve, reject) => { - // check for basic license first for consistency with other - // security disabled checks - if (isSecurityDisabled(xpackMainPlugin)) { - getNodeCount() - .then(resolve) - .catch(reject); - } else { - // if security is enabled, check that the user has permission to - // view jobs before calling getNodeCount. - // getNodeCount calls the _nodes endpoint as the internal user - // and so could give the user access to more information than - // they are entitled to. - const body = { - cluster: [ - 'cluster:monitor/xpack/ml/job/get', - 'cluster:monitor/xpack/ml/job/stats/get', - 'cluster:monitor/xpack/ml/datafeeds/get', - 'cluster:monitor/xpack/ml/datafeeds/stats/get', - ], - }; - callWithRequest('ml.privilegeCheck', { body }) - .then(resp => { - if ( - resp.cluster['cluster:monitor/xpack/ml/job/get'] && - resp.cluster['cluster:monitor/xpack/ml/job/stats/get'] && - resp.cluster['cluster:monitor/xpack/ml/datafeeds/get'] && - resp.cluster['cluster:monitor/xpack/ml/datafeeds/stats/get'] - ) { - getNodeCount() - .then(resolve) - .catch(reject); - } else { - // if the user doesn't have permission to create jobs - // return a 403 - reject(Boom.forbidden()); - } - }) - .catch(reject); - } - }).catch(error => wrapError(error)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'GET', - path: '/api/ml/info', - async handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - - try { - const info = await callWithRequest('ml.info'); - const cloudId = cloud && cloud.cloudId; - return { ...info, cloudId }; - } catch (error) { - return wrapError(error); - } - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/es_search', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - return callWithRequest('search', request.payload).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); -} diff --git a/x-pack/legacy/plugins/ml/server/routes/system.ts b/x-pack/legacy/plugins/ml/server/routes/system.ts new file mode 100644 index 00000000000000..5861b53d74875c --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/routes/system.ts @@ -0,0 +1,247 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +import { Request } from 'hapi'; +import { RequestHandlerContext } from 'kibana/server'; +import { wrapError } from '../client/error_wrapper'; +import { mlLog } from '../client/log'; +import { privilegesProvider } from '../lib/check_privileges'; +import { isSecurityDisabled } from '../lib/security_utils'; +import { spacesUtilsProvider } from '../lib/spaces_utils'; +import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory'; +import { RouteInitialization } from '../new_platform/plugin'; + +/** + * System routes + */ +export function systemRoutes({ + router, + xpackMainPlugin, + spacesPlugin, + cloud, +}: RouteInitialization) { + async function getNodeCount(context: RequestHandlerContext) { + const filterPath = 'nodes.*.attributes'; + const resp = await context.ml!.mlClient.callAsInternalUser('nodes.info', { + filterPath, + }); + + let count = 0; + if (typeof resp.nodes === 'object') { + Object.keys(resp.nodes).forEach(k => { + if (resp.nodes[k].attributes !== undefined) { + const maxOpenJobs = resp.nodes[k].attributes['ml.max_open_jobs']; + if (maxOpenJobs !== null && maxOpenJobs > 0) { + count++; + } + } + }); + } + return { count }; + } + + /** + * @apiGroup SystemRoutes + * + * @api {post} /api/ml/_has_privileges Check privileges + * @apiName HasPrivileges + * @apiDescription Checks if the user has required privileges + */ + router.post( + { + path: '/api/ml/_has_privileges', + validate: { + body: schema.maybe(schema.any()), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + let upgradeInProgress = false; + try { + const info = await context.ml!.mlClient.callAsCurrentUser('ml.info'); + // if ml indices are currently being migrated, upgrade_mode will be set to true + // pass this back with the privileges to allow for the disabling of UI controls. + upgradeInProgress = info.upgrade_mode === true; + } catch (error) { + // if the ml.info check fails, it could be due to the user having insufficient privileges + // most likely they do not have the ml_user role and therefore will be blocked from using + // ML at all. However, we need to catch this error so the privilege check doesn't fail. + if (error.status === 403) { + mlLog.info( + 'Unable to determine whether upgrade is being performed due to insufficient user privileges' + ); + } else { + mlLog.warn('Unable to determine whether upgrade is being performed'); + } + } + + if (isSecurityDisabled(xpackMainPlugin)) { + // if xpack.security.enabled has been explicitly set to false + // return that security is disabled and don't call the privilegeCheck endpoint + return response.ok({ + body: { + securityDisabled: true, + upgradeInProgress, + }, + }); + } else { + const body = request.body; + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.privilegeCheck', { body }); + resp.upgradeInProgress = upgradeInProgress; + return response.ok({ + body: resp, + }); + } + } catch (error) { + return response.customError(wrapError(error)); + } + }) + ); + + /** + * @apiGroup SystemRoutes + * + * @api {get} /api/ml/ml_capabilities Check ML capabilities + * @apiName MlCapabilities + * @apiDescription Checks ML capabilities + */ + router.get( + { + path: '/api/ml/ml_capabilities', + validate: { + query: schema.object({ + ignoreSpaces: schema.maybe(schema.string()), + }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const ignoreSpaces = request.query && request.query.ignoreSpaces === 'true'; + // if spaces is disabled force isMlEnabledInSpace to be true + const { isMlEnabledInSpace } = + spacesPlugin !== undefined + ? spacesUtilsProvider(spacesPlugin, (request as unknown) as Request) + : { isMlEnabledInSpace: async () => true }; + + const { getPrivileges } = privilegesProvider( + context.ml!.mlClient.callAsCurrentUser, + xpackMainPlugin, + isMlEnabledInSpace, + ignoreSpaces + ); + return response.ok({ + body: await getPrivileges(), + }); + } catch (error) { + return response.customError(wrapError(error)); + } + }) + ); + + /** + * @apiGroup SystemRoutes + * + * @api {get} /api/ml/ml_node_count Get the amount of ML nodes + * @apiName MlNodeCount + * @apiDescription Returns the amount of ML nodes. + */ + router.get( + { + path: '/api/ml/ml_node_count', + validate: false, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + // check for basic license first for consistency with other + // security disabled checks + if (isSecurityDisabled(xpackMainPlugin)) { + return response.ok({ + body: await getNodeCount(context), + }); + } else { + // if security is enabled, check that the user has permission to + // view jobs before calling getNodeCount. + // getNodeCount calls the _nodes endpoint as the internal user + // and so could give the user access to more information than + // they are entitled to. + const requiredPrivileges = [ + 'cluster:monitor/xpack/ml/job/get', + 'cluster:monitor/xpack/ml/job/stats/get', + 'cluster:monitor/xpack/ml/datafeeds/get', + 'cluster:monitor/xpack/ml/datafeeds/stats/get', + ]; + const body = { cluster: requiredPrivileges }; + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.privilegeCheck', { body }); + + if (resp.has_all_requested) { + return response.ok({ + body: await getNodeCount(context), + }); + } else { + // if the user doesn't have permission to create jobs + // return a 403 + return response.forbidden(); + } + } + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup SystemRoutes + * + * @api {get} /api/ml/info Get ML info + * @apiName MlInfo + * @apiDescription Returns defaults and limits used by machine learning. + */ + router.get( + { + path: '/api/ml/info', + validate: false, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const info = await context.ml!.mlClient.callAsCurrentUser('ml.info'); + const cloudId = cloud && cloud.cloudId; + return response.ok({ + body: { ...info, cloudId }, + }); + } catch (error) { + return response.customError(wrapError(error)); + } + }) + ); + + /** + * @apiGroup SystemRoutes + * + * @apiDeprecated + * + * @api {post} /api/ml/es_search ES Search wrapper + * @apiName MlEsSearch + */ + router.post( + { + path: '/api/ml/es_search', + validate: { + body: schema.maybe(schema.any()), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + return response.ok({ + body: await context.ml!.mlClient.callAsCurrentUser('search', request.body), + }); + } catch (error) { + return response.customError(wrapError(error)); + } + }) + ); +} From c0eba49de7e1511c8ec679c911c90fe4741ae2eb Mon Sep 17 00:00:00 2001 From: Rachel Shen Date: Thu, 20 Feb 2020 13:46:22 -0700 Subject: [PATCH 03/19] Clarify Precision function in Timelion Kibana (#58031) (#58142) * Closes issue 26100 Co-authored-by: Elastic Machine --- src/plugins/timelion/server/series_functions/precision.js | 4 ++-- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/plugins/timelion/server/series_functions/precision.js b/src/plugins/timelion/server/series_functions/precision.js index 756fb067f2335c..71c15fdd46fddc 100644 --- a/src/plugins/timelion/server/series_functions/precision.js +++ b/src/plugins/timelion/server/series_functions/precision.js @@ -32,12 +32,12 @@ export default new Chainable('precision', { name: 'precision', types: ['number'], help: i18n.translate('timelion.help.functions.precision.args.precisionHelpText', { - defaultMessage: 'Number of digits to round each value to', + defaultMessage: 'The number of digits to truncate each value to', }), }, ], help: i18n.translate('timelion.help.functions.precisionHelpText', { - defaultMessage: 'number of digits to round the decimal portion of the value to', + defaultMessage: 'The number of digits to truncate the decimal portion of the value to', }), fn: async function precisionFn(args) { await alter(args, function(eachSeries, precision) { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 90dd1753bb7cf5..958e40125fefc4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2698,8 +2698,6 @@ "timelion.help.functions.points.args.symbolHelpText": "点のシンボルです。{validSymbols} の 1 つ", "timelion.help.functions.points.args.weightHelpText": "点の周りの太さです", "timelion.help.functions.pointsHelpText": "数列を点として表示します", - "timelion.help.functions.precision.args.precisionHelpText": "各値を四捨五入する桁数です", - "timelion.help.functions.precisionHelpText": "値の小数点以下の四捨五入する桁数です", "timelion.help.functions.props.args.globalHelpText": "各数列に対し、seriesList にプロップを設定します", "timelion.help.functions.propsHelpText": "数列に任意のプロパティを設定するため、自己責任で行ってください。例: {example}", "timelion.help.functions.quandl.args.codeHelpText": "プロットする Quandl コードです。これらは quandl.com に掲載されています。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c4dccd812c0fa0..a68658b86e5e8e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2698,8 +2698,6 @@ "timelion.help.functions.points.args.symbolHelpText": "点符号。以下选项之一:{validSymbols}", "timelion.help.functions.points.args.weightHelpText": "围绕点的线条粗细", "timelion.help.functions.pointsHelpText": "将序列显示为点", - "timelion.help.functions.precision.args.precisionHelpText": "将每个值舍入到的小数位数", - "timelion.help.functions.precisionHelpText": "将值的小数部分舍入到的小数位数", "timelion.help.functions.props.args.globalHelpText": "在 seriesList 与每个序列上设置属性", "timelion.help.functions.propsHelpText": "在序列上可设置任意属性,但请自担风险。例如 {example}", "timelion.help.functions.quandl.args.codeHelpText": "要绘图的 quandl 代码。可以在 quandl.com 找到这些内容。", From cd87aab310507912ba572a307776613e3ac4f0c3 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Thu, 20 Feb 2020 14:04:48 -0700 Subject: [PATCH 04/19] [skip-ci] Fix broken links to saved objects APIs in MIGRATION.md (#58033) (#58179) --- src/core/MIGRATION.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 9e57fc4c368762..a0401f379d09ef 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -1200,9 +1200,9 @@ In server code, `core` can be accessed from either `server.newPlatform` or `kbnS | `server.plugins.elasticsearch.getCluster('data')` | [`context.core.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | | `server.plugins.elasticsearch.getCluster('admin')` | [`context.core.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | | `server.plugins.elasticsearch.createCluster(...)` | [`core.elasticsearch.createClient`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md) | | -| `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactory`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md) | | +| `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactoryProvider`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) | | | `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | | -| `server.savedObjects.getSavedObjectsRepository` | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createscopedrepository.md) | | +| `server.savedObjects.getSavedObjectsRepository` | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md) | | | `server.savedObjects.getScopedSavedObjectsClient` | [`core.savedObjects.getScopedClient`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.getscopedclient.md) | | | `request.getSavedObjectsClient` | [`context.core.savedObjects.client`](/docs/development/core/server/kibana-plugin-server.requesthandlercontext.core.md) | | | `request.getUiSettingsService` | [`context.uiSettings.client`](/docs/development/core/server/kibana-plugin-server.iuisettingsclient.md) | | From 9ab75a385a00e50bca06c589a744739811f95256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Thu, 20 Feb 2020 16:22:11 -0500 Subject: [PATCH 05/19] Skip flaky alert details test (#58120) (#58144) * Skip flaky test * Skip suite * Skip suite --- .../functional_with_es_ssl/apps/triggers_actions_ui/details.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 3db4731f0adfb6..86fc3d6cd6a6c0 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -18,7 +18,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const alerting = getService('alerting'); const retry = getService('retry'); - describe('Alert Details', function() { + // FLAKY: https://github.com/elastic/kibana/issues/57426 + describe.skip('Alert Details', function() { describe('Header', function() { const testRunUuid = uuid.v4(); before(async () => { From e6a3f988f6afff547cdd27111bfbdcb7c4945056 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 20 Feb 2020 15:29:39 -0600 Subject: [PATCH 06/19] Fix browser date format (#57714) (#58146) * fix browser date formatter --- .../constants/base_formatters.ts | 2 -- .../common/field_formats/converters/index.ts | 1 - .../field_formats/field_formats_registry.ts | 4 +++ .../data/common/field_formats/index.ts | 1 - .../data/common/field_formats/types.ts | 1 + src/plugins/data/common/types.ts | 1 + .../data/public/field_formats/constants.ts | 23 +++++++++++++ .../field_formats/converters/date.test.ts | 0 .../field_formats/converters/date.ts | 9 +++-- .../public/field_formats/converters/index.ts | 20 +++++++++++ .../field_formats_service.test.ts | 34 +++++++++++++++++++ .../field_formats/field_formats_service.ts | 19 +++++++---- .../data/public/field_formats/index.ts | 2 ++ src/plugins/data/public/index.ts | 4 ++- src/plugins/data/public/mocks.ts | 1 + .../query_string_input.test.tsx.snap | 6 ++++ .../field_formats/converters/date_server.ts | 6 ++-- .../server/field_formats/converters/index.ts | 20 +++++++++++ .../field_formats_service.test.ts | 34 +++++++++++++++++++ .../field_formats/field_formats_service.ts | 3 +- src/plugins/data/server/index.ts | 4 --- src/test_utils/public/stub_field_formats.ts | 3 +- 22 files changed, 174 insertions(+), 24 deletions(-) create mode 100644 src/plugins/data/public/field_formats/constants.ts rename src/plugins/data/{common => public}/field_formats/converters/date.test.ts (100%) rename src/plugins/data/{common => public}/field_formats/converters/date.ts (92%) create mode 100644 src/plugins/data/public/field_formats/converters/index.ts create mode 100644 src/plugins/data/public/field_formats/field_formats_service.test.ts rename src/plugins/data/{common => server}/field_formats/converters/date_server.ts (95%) create mode 100644 src/plugins/data/server/field_formats/converters/index.ts create mode 100644 src/plugins/data/server/field_formats/field_formats_service.test.ts diff --git a/src/plugins/data/common/field_formats/constants/base_formatters.ts b/src/plugins/data/common/field_formats/constants/base_formatters.ts index 95aedd02d16d65..6befe8cea71f54 100644 --- a/src/plugins/data/common/field_formats/constants/base_formatters.ts +++ b/src/plugins/data/common/field_formats/constants/base_formatters.ts @@ -23,7 +23,6 @@ import { BoolFormat, BytesFormat, ColorFormat, - DateFormat, DateNanosFormat, DurationFormat, IpFormat, @@ -41,7 +40,6 @@ export const baseFormatters: IFieldFormatType[] = [ BoolFormat, BytesFormat, ColorFormat, - DateFormat, DateNanosFormat, DurationFormat, IpFormat, diff --git a/src/plugins/data/common/field_formats/converters/index.ts b/src/plugins/data/common/field_formats/converters/index.ts index f7e50539b44d8e..cc9fae7fc9965d 100644 --- a/src/plugins/data/common/field_formats/converters/index.ts +++ b/src/plugins/data/common/field_formats/converters/index.ts @@ -19,7 +19,6 @@ export { UrlFormat } from './url'; export { BytesFormat } from './bytes'; -export { DateFormat } from './date_server'; export { DateNanosFormat } from './date_nanos'; export { RelativeDateFormat } from './relative_date'; export { DurationFormat } from './duration'; diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts index 9fe9a31307b6ac..9fdf1ad9c80fbf 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.ts +++ b/src/plugins/data/common/field_formats/field_formats_registry.ts @@ -95,6 +95,10 @@ export class FieldFormatsRegistry { return undefined; }; + getTypeWithoutMetaParams = (formatId: FieldFormatId): IFieldFormatType | undefined => { + return this.fieldFormats.get(formatId); + }; + /** * Get the default FieldFormat type (class) for * a field type, using the format:defaultTypeMap. diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts index d7858966f2620a..13d3d9d73d43a6 100644 --- a/src/plugins/data/common/field_formats/index.ts +++ b/src/plugins/data/common/field_formats/index.ts @@ -27,7 +27,6 @@ export { BoolFormat, BytesFormat, ColorFormat, - DateFormat, DateNanosFormat, DurationFormat, IpFormat, diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts index 24aa92c67b6941..0c16d9f1ac8bf7 100644 --- a/src/plugins/data/common/field_formats/types.ts +++ b/src/plugins/data/common/field_formats/types.ts @@ -18,6 +18,7 @@ */ import { FieldFormat } from './field_format'; +export { FieldFormat }; /** @public **/ export type FieldFormatsContentType = 'html' | 'text'; diff --git a/src/plugins/data/common/types.ts b/src/plugins/data/common/types.ts index be0d3230b3a0e0..93629c3dbaf626 100644 --- a/src/plugins/data/common/types.ts +++ b/src/plugins/data/common/types.ts @@ -21,3 +21,4 @@ export * from './timefilter/types'; export * from './query/types'; export * from './kbn_field_types/types'; export * from './index_patterns/types'; +export { TextContextTypeConvert, IFieldFormatMetaParams } from './field_formats/types'; diff --git a/src/plugins/data/public/field_formats/constants.ts b/src/plugins/data/public/field_formats/constants.ts new file mode 100644 index 00000000000000..a5c2b4e3799083 --- /dev/null +++ b/src/plugins/data/public/field_formats/constants.ts @@ -0,0 +1,23 @@ +/* + * 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 { baseFormatters } from '../../common'; +import { DateFormat } from './converters/date'; + +export const baseFormattersPublic = [DateFormat, ...baseFormatters]; diff --git a/src/plugins/data/common/field_formats/converters/date.test.ts b/src/plugins/data/public/field_formats/converters/date.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/date.test.ts rename to src/plugins/data/public/field_formats/converters/date.test.ts diff --git a/src/plugins/data/common/field_formats/converters/date.ts b/src/plugins/data/public/field_formats/converters/date.ts similarity index 92% rename from src/plugins/data/common/field_formats/converters/date.ts rename to src/plugins/data/public/field_formats/converters/date.ts index 3888df051b1187..3e1efdc69dec82 100644 --- a/src/plugins/data/common/field_formats/converters/date.ts +++ b/src/plugins/data/public/field_formats/converters/date.ts @@ -20,9 +20,12 @@ import { i18n } from '@kbn/i18n'; import { memoize, noop } from 'lodash'; import moment from 'moment'; -import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; -import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; +import { + FieldFormat, + KBN_FIELD_TYPES, + TextContextTypeConvert, + FIELD_FORMAT_IDS, +} from '../../../common'; export class DateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE; diff --git a/src/plugins/data/public/field_formats/converters/index.ts b/src/plugins/data/public/field_formats/converters/index.ts new file mode 100644 index 00000000000000..c51111092becad --- /dev/null +++ b/src/plugins/data/public/field_formats/converters/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { DateFormat } from './date'; diff --git a/src/plugins/data/public/field_formats/field_formats_service.test.ts b/src/plugins/data/public/field_formats/field_formats_service.test.ts new file mode 100644 index 00000000000000..e066af28f4699c --- /dev/null +++ b/src/plugins/data/public/field_formats/field_formats_service.test.ts @@ -0,0 +1,34 @@ +/* + * 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 { FieldFormatsService } from './field_formats_service'; +import { coreMock } from '../../../../../src/core/public/mocks'; +import { DateFormat } from './converters/date'; + +describe('FieldFormatService', () => { + test('DateFormat is public version', () => { + const mockCore = coreMock.createSetup(); + const service = new FieldFormatsService(); + service.setup(mockCore); + const fieldFormatsRegistry = service.start(); + const DateFormatFromRegsitry = fieldFormatsRegistry.getTypeWithoutMetaParams('date'); + + expect(DateFormatFromRegsitry).toEqual(DateFormat); + }); +}); diff --git a/src/plugins/data/public/field_formats/field_formats_service.ts b/src/plugins/data/public/field_formats/field_formats_service.ts index 785bedf9b35d34..22c7e90c06130b 100644 --- a/src/plugins/data/public/field_formats/field_formats_service.ts +++ b/src/plugins/data/public/field_formats/field_formats_service.ts @@ -18,9 +18,10 @@ */ import { CoreSetup } from 'src/core/public'; -import { FieldFormatsRegistry } from '../../common/field_formats'; +import { FieldFormatsRegistry } from '../../common'; import { deserializeFieldFormat } from './utils/deserialize'; import { FormatFactory } from '../../common/field_formats/utils'; +import { baseFormattersPublic } from './constants'; export class FieldFormatsService { private readonly fieldFormatsRegistry: FieldFormatsRegistry = new FieldFormatsRegistry(); @@ -34,13 +35,17 @@ export class FieldFormatsService { const getConfig = core.uiSettings.get.bind(core.uiSettings); - this.fieldFormatsRegistry.init(getConfig, { - parsedUrl: { - origin: window.location.origin, - pathname: window.location.pathname, - basePath: core.http.basePath.get(), + this.fieldFormatsRegistry.init( + getConfig, + { + parsedUrl: { + origin: window.location.origin, + pathname: window.location.pathname, + basePath: core.http.basePath.get(), + }, }, - }); + baseFormattersPublic + ); return this.fieldFormatsRegistry as FieldFormatsSetup; } diff --git a/src/plugins/data/public/field_formats/index.ts b/src/plugins/data/public/field_formats/index.ts index 4550a5781535f2..015d5b39561bb5 100644 --- a/src/plugins/data/public/field_formats/index.ts +++ b/src/plugins/data/public/field_formats/index.ts @@ -18,3 +18,5 @@ */ export { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats_service'; +export { DateFormat } from './converters'; +export { baseFormattersPublic } from './constants'; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index cdc4167f545af5..cbd4bfd348797d 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -156,7 +156,6 @@ import { BoolFormat, BytesFormat, ColorFormat, - DateFormat, DateNanosFormat, DurationFormat, IpFormat, @@ -171,6 +170,9 @@ import { serializeFieldFormat, } from '../common/field_formats'; +import { DateFormat } from './field_formats'; +export { baseFormattersPublic } from './field_formats'; + // Field formats helpers namespace: export const fieldFormats = { FieldFormat, diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 2d5cc72597ec4b..a2a1a2424fc90e 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -50,6 +50,7 @@ const fieldFormatsMock: IFieldFormatsRegistry = { register: jest.fn(), parseDefaultTypeMap: jest.fn(), deserialize: jest.fn(), + getTypeWithoutMetaParams: jest.fn(), }; const createSetupContract = (): Setup => { diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap index 06e56aaf3eb0a6..93af543fba1a8b 100644 --- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap +++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap @@ -184,6 +184,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA "getInstance": [MockFunction], "getType": [MockFunction], "getTypeNameByEsTypes": [MockFunction], + "getTypeWithoutMetaParams": [MockFunction], "init": [MockFunction], "parseDefaultTypeMap": [MockFunction], "register": [MockFunction], @@ -839,6 +840,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA "getInstance": [MockFunction], "getType": [MockFunction], "getTypeNameByEsTypes": [MockFunction], + "getTypeWithoutMetaParams": [MockFunction], "init": [MockFunction], "parseDefaultTypeMap": [MockFunction], "register": [MockFunction], @@ -1476,6 +1478,7 @@ exports[`QueryStringInput Should pass the query language to the language switche "getInstance": [MockFunction], "getType": [MockFunction], "getTypeNameByEsTypes": [MockFunction], + "getTypeWithoutMetaParams": [MockFunction], "init": [MockFunction], "parseDefaultTypeMap": [MockFunction], "register": [MockFunction], @@ -2128,6 +2131,7 @@ exports[`QueryStringInput Should pass the query language to the language switche "getInstance": [MockFunction], "getType": [MockFunction], "getTypeNameByEsTypes": [MockFunction], + "getTypeWithoutMetaParams": [MockFunction], "init": [MockFunction], "parseDefaultTypeMap": [MockFunction], "register": [MockFunction], @@ -2765,6 +2769,7 @@ exports[`QueryStringInput Should render the given query 1`] = ` "getInstance": [MockFunction], "getType": [MockFunction], "getTypeNameByEsTypes": [MockFunction], + "getTypeWithoutMetaParams": [MockFunction], "init": [MockFunction], "parseDefaultTypeMap": [MockFunction], "register": [MockFunction], @@ -3417,6 +3422,7 @@ exports[`QueryStringInput Should render the given query 1`] = ` "getInstance": [MockFunction], "getType": [MockFunction], "getTypeNameByEsTypes": [MockFunction], + "getTypeWithoutMetaParams": [MockFunction], "init": [MockFunction], "parseDefaultTypeMap": [MockFunction], "register": [MockFunction], diff --git a/src/plugins/data/common/field_formats/converters/date_server.ts b/src/plugins/data/server/field_formats/converters/date_server.ts similarity index 95% rename from src/plugins/data/common/field_formats/converters/date_server.ts rename to src/plugins/data/server/field_formats/converters/date_server.ts index 216af133bb5f52..f4e62962591963 100644 --- a/src/plugins/data/common/field_formats/converters/date_server.ts +++ b/src/plugins/data/server/field_formats/converters/date_server.ts @@ -20,14 +20,14 @@ import { i18n } from '@kbn/i18n'; import { memoize, noop } from 'lodash'; import moment from 'moment-timezone'; -import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; -import { FieldFormat } from '../field_format'; import { + FieldFormat, + KBN_FIELD_TYPES, TextContextTypeConvert, FIELD_FORMAT_IDS, FieldFormatsGetConfigFn, IFieldFormatMetaParams, -} from '../types'; +} from '../../../common'; export class DateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE; diff --git a/src/plugins/data/server/field_formats/converters/index.ts b/src/plugins/data/server/field_formats/converters/index.ts new file mode 100644 index 00000000000000..f5c69df9728699 --- /dev/null +++ b/src/plugins/data/server/field_formats/converters/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { DateFormat } from './date_server'; diff --git a/src/plugins/data/server/field_formats/field_formats_service.test.ts b/src/plugins/data/server/field_formats/field_formats_service.test.ts new file mode 100644 index 00000000000000..2e7ce0fa435a76 --- /dev/null +++ b/src/plugins/data/server/field_formats/field_formats_service.test.ts @@ -0,0 +1,34 @@ +/* + * 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 { FieldFormatsService } from './field_formats_service'; +import { DateFormat } from './converters/date_server'; +import { coreMock } from '../../../../core/server/mocks'; + +describe('FieldFormatService', () => { + test('DateFormat is server version', async () => { + const service = new FieldFormatsService(); + const fieldFormatsService = await service.start(); + const uiSettings = coreMock.createStart().uiSettings.asScopedToClient({} as any); + const fieldFormatsRegistry = await fieldFormatsService.fieldFormatServiceFactory(uiSettings); + const DateFormatFromRegsitry = fieldFormatsRegistry.getTypeWithoutMetaParams('date'); + + expect(DateFormatFromRegsitry).toEqual(DateFormat); + }); +}); diff --git a/src/plugins/data/server/field_formats/field_formats_service.ts b/src/plugins/data/server/field_formats/field_formats_service.ts index a31e5927ab8001..0dac64fb5dc1d2 100644 --- a/src/plugins/data/server/field_formats/field_formats_service.ts +++ b/src/plugins/data/server/field_formats/field_formats_service.ts @@ -19,9 +19,10 @@ import { has } from 'lodash'; import { FieldFormatsRegistry, IFieldFormatType, baseFormatters } from '../../common/field_formats'; import { IUiSettingsClient } from '../../../../core/server'; +import { DateFormat } from './converters'; export class FieldFormatsService { - private readonly fieldFormatClasses: IFieldFormatType[] = baseFormatters; + private readonly fieldFormatClasses: IFieldFormatType[] = [DateFormat, ...baseFormatters]; public setup() { return { diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 3ee98a318de352..40d367138b60d7 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -83,7 +83,6 @@ import { BoolFormat, BytesFormat, ColorFormat, - DateFormat, DateNanosFormat, DurationFormat, IpFormat, @@ -101,13 +100,10 @@ import { export const fieldFormats = { FieldFormatsRegistry, FieldFormat, - serializeFieldFormat, - BoolFormat, BytesFormat, ColorFormat, - DateFormat, DateNanosFormat, DurationFormat, IpFormat, diff --git a/src/test_utils/public/stub_field_formats.ts b/src/test_utils/public/stub_field_formats.ts index 5a20823134ebd5..589e93fd600c2c 100644 --- a/src/test_utils/public/stub_field_formats.ts +++ b/src/test_utils/public/stub_field_formats.ts @@ -19,12 +19,13 @@ import { CoreSetup } from 'kibana/public'; import { DataPublicPluginStart, fieldFormats } from '../../plugins/data/public'; import { deserializeFieldFormat } from '../../plugins/data/public/field_formats/utils/deserialize'; +import { baseFormattersPublic } from '../../plugins/data/public'; export const getFieldFormatsRegistry = (core: CoreSetup) => { const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); const getConfig = core.uiSettings.get.bind(core.uiSettings); - fieldFormatsRegistry.init(getConfig, {}); + fieldFormatsRegistry.init(getConfig, {}, baseFormattersPublic); fieldFormatsRegistry.deserialize = deserializeFieldFormat.bind( fieldFormatsRegistry as DataPublicPluginStart['fieldFormats'] From 24e5d4cfe4237e7aca5f65c24ebb8230570d2dfe Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Thu, 20 Feb 2020 16:06:45 -0600 Subject: [PATCH 07/19] Add throttle param to Alerting readme (#57609) (#58162) Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- x-pack/plugins/alerting/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index 32ca804198ebd4..6b04b3ba500b08 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -249,6 +249,7 @@ Payload: |tags|A list of keywords to reference and search in the future.|string[]| |alertTypeId|The id value of the alert type you want to call when the alert is scheduled to execute.|string| |schedule|The schedule specifying when this alert should be run, using one of the available schedule formats specified under _Schedule Formats_ below|object| +|throttle|A Duration specifying how often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications over this period.|string| |params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object| |actions|Array of the following:
- `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value.
- `id` (string): The id of the action saved object to execute.
- `params` (object): The map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array| @@ -299,6 +300,7 @@ Payload: |Property|Description|Type| |---|---|---| |schedule|The schedule specifying when this alert should be run, using one of the available schedule formats specified under _Schedule Formats_ below|object| +|throttle|A Duration specifying how often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications over this period.|string| |name|A name to reference and search in the future.|string| |tags|A list of keywords to reference and search in the future.|string[]| |params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object| From 534c468fd57d2897dcd0da0807694957e7024096 Mon Sep 17 00:00:00 2001 From: Maggie Ghamry <46542915+maggieghamry@users.noreply.github.com> Date: Thu, 20 Feb 2020 17:10:15 -0500 Subject: [PATCH 08/19] [Canvas] Adds argument to open all links in new tab within markdown element (#57017) (#58149) * Adds toggle to open links in new tab within markdown element * Updating test for markdown function * Switch to using Markdown Component * Update ui.ts Update to change toggle verbiage per Ryan's request, and reuse the Kibana Markdown per Corey's help and recommendation. Still working on updating the styles (consulting Ryan) * Update toggle.js Update to prevent text for "Open links in new tab from wrapping" - the example from the horizontal bar chart is quite differently, and reads from "axisConfig" - when I changed the argType to "axisConfig", the layout was the same, but I'll need some input on which specific styles to add to the "ToggleArgInput" - I think this is where the style updates need to occur to get the toggle to stay on the same line, but may be wrong. * Update ui.ts Update to original message string per Ryan's feedback, now that there is no wrapping * Update to UI styles per Ryan's feedback * updating message per Ryan's request * Update ui.ts update to fix internationalization issues Co-authored-by: Corey Robertson Co-authored-by: Corey Robertson Co-authored-by: Elastic Machine --- .../functions/browser/markdown.test.js | 29 ++++++++++++++++- .../functions/browser/markdown.ts | 8 +++++ .../renderers/markdown/index.js | 14 +++------ .../canvas_plugin_src/uis/arguments/toggle.js | 31 +++++++++++-------- .../canvas_plugin_src/uis/views/markdown.js | 11 +++++++ .../canvas/i18n/functions/dict/markdown.ts | 4 +++ x-pack/legacy/plugins/canvas/i18n/ui.ts | 12 +++++++ 7 files changed, 86 insertions(+), 23 deletions(-) diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js index f9912c270a46a0..27ea290fb4dccd 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js @@ -19,7 +19,7 @@ describe('markdown', () => { }); describe('args', () => { - describe('expression', () => { + describe('content', () => { it('sets the content to all strings in expression concatenated', () => { const result = fn(null, { content: ['# this ', 'is ', 'some ', 'markdown'], @@ -54,5 +54,32 @@ describe('markdown', () => { // TODO: write test when using an instance of the interpreter // it("defaults to the expression '{font}'", () => {}); }); + describe('openLinksInNewTab', () => { + it('sets the value of openLinksInNewTab to true ', () => { + const result = fn(null, { + content: ['some ', 'markdown'], + openLinksInNewTab: true, + }); + + expect(result.value).toHaveProperty('openLinksInNewTab', true); + }); + + it('sets the value of openLinksInNewTab to false ', () => { + const result = fn(null, { + content: ['some ', 'markdown'], + openLinksInNewTab: false, + }); + + expect(result.value).toHaveProperty('openLinksInNewTab', false); + }); + + it('defaults the value of openLinksInNewTab to false ', () => { + const result = fn(null, { + content: ['some ', 'markdown'], + }); + + expect(result.value).toHaveProperty('openLinksInNewTab', false); + }); + }); }); }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts index 95859feeed5f3e..e94b9f201a1746 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts @@ -19,11 +19,13 @@ type Context = Datatable | null; interface Arguments { content: string[]; font: Style; + openLinksInNewTab: boolean; } interface Return { content: string; font: Style; + openLinksInNewTab: boolean; } export function markdown(): ExpressionFunctionDefinition< @@ -53,6 +55,11 @@ export function markdown(): ExpressionFunctionDefinition< help: argHelp.font, default: '{font}', }, + openLinksInNewTab: { + types: ['boolean'], + help: argHelp.openLinksInNewTab, + default: false, + }, }, fn: (input, args) => { const compileFunctions = args.content.map(str => @@ -71,6 +78,7 @@ export function markdown(): ExpressionFunctionDefinition< value: { content: compileFunctions.map(fn => fn(ctx)).join(''), font: args.font, + openLinksInNewTab: args.openLinksInNewTab, }, }; }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js index 82c63d5e7d5295..c1bfd7c99ac413 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js @@ -6,33 +6,29 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import Markdown from 'markdown-it'; import { RendererStrings } from '../../../i18n'; +import { Markdown } from '../../../../../../../src/legacy/core_plugins/kibana_react/public'; const { markdown: strings } = RendererStrings; -const md = new Markdown(); - export const markdown = () => ({ name: 'markdown', displayName: strings.getDisplayName(), help: strings.getHelpDescription(), reuseDomNode: true, render(domNode, config, handlers) { - const html = { __html: md.render(String(config.content)) }; const fontStyle = config.font ? config.font.spec : {}; - /* eslint-disable react/no-danger */ ReactDOM.render( -
, domNode, () => handlers.done() ); - /* eslint-enable */ handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js index bcad4678e0b6a8..299f96ff1b4e8a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js @@ -19,18 +19,21 @@ const ToggleArgInput = ({ onValueChange, argValue, argId, renderError, typeInsta return null; } return ( - - - +
+ + + +
); }; @@ -38,6 +41,8 @@ ToggleArgInput.propTypes = { onValueChange: PropTypes.func.isRequired, argValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.string, PropTypes.object]).isRequired, argId: PropTypes.string.isRequired, + labelValue: PropTypes.string, + showLabelValue: PropTypes.bool, renderError: PropTypes.func.isRequired, }; @@ -45,6 +50,6 @@ export const toggle = () => ({ name: 'toggle', displayName: strings.getDisplayName(), help: strings.getHelp(), - simpleTemplate: templateFromReactComponent(ToggleArgInput), + template: templateFromReactComponent(ToggleArgInput), default: 'false', }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js index 1c46bc6dd57c2a..edae739ee0d3da 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js @@ -29,5 +29,16 @@ export const markdown = () => ({ name: 'font', argType: 'font', }, + { + name: 'openLinksInNewTab', + displayName: strings.getOpenLinksInNewTabDisplayName(), + help: strings.getOpenLinksInNewTabHelp(), + label: strings.getOpenLinksInNewTabLabelName(), + argType: 'toggle', + default: false, + options: { + labelValue: 'Open all links in a new tab', + }, + }, ], }); diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts index d5271e14436e23..aa2845ba4ec3a0 100644 --- a/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts @@ -37,5 +37,9 @@ export const help: FunctionHelp> = { fontWeight: 'font-weight', }, }), + openLinksInNewTab: i18n.translate('xpack.canvas.functions.markdown.args.openLinkHelpText', { + defaultMessage: + 'A true/false value for opening links in a new tab. Default value is false. Setting to true will open all links in a new tab.', + }), }, }; diff --git a/x-pack/legacy/plugins/canvas/i18n/ui.ts b/x-pack/legacy/plugins/canvas/i18n/ui.ts index 323a6c97fd967d..5b94cb0435b31f 100644 --- a/x-pack/legacy/plugins/canvas/i18n/ui.ts +++ b/x-pack/legacy/plugins/canvas/i18n/ui.ts @@ -628,6 +628,18 @@ export const ViewStrings = { markdown: MARKDOWN, }, }), + getOpenLinksInNewTabDisplayName: () => + i18n.translate('xpack.canvas.uis.views.openLinksInNewTabTitle', { + defaultMessage: 'Markdown link settings', + }), + getOpenLinksInNewTabLabelName: () => + i18n.translate('xpack.canvas.uis.views.openLinksInNewTabLabel', { + defaultMessage: 'Open all links in a new tab', + }), + getOpenLinksInNewTabHelp: () => + i18n.translate('xpack.canvas.uis.views.openLinksInNewTabHelpLabel', { + defaultMessage: 'Set links to open in new tab', + }), }, Metric: { getDisplayName: () => From 23e3ce78f7f67f8042e71786903006dea69688c6 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 20 Feb 2020 16:51:12 -0700 Subject: [PATCH 09/19] [7.x] add monaco to kbn/ui-shared-deps and load required features for all uses (#58075) (#58167) * add monaco to kbn/ui-shared-deps and load required features for all uses (#58075) * add monaco to kbn/ui-shared-deps and load required features for all uses * forgot to save a change * remove unused imports * include a cache buster to counteract issue #58077 * include monaco.ts in ts project * update yarn.lock --- packages/kbn-optimizer/src/index.ts | 1 + packages/kbn-ui-shared-deps/entry.js | 2 + packages/kbn-ui-shared-deps/index.js | 3 ++ packages/kbn-ui-shared-deps/monaco.ts | 32 ++++++++++++ packages/kbn-ui-shared-deps/package.json | 2 +- packages/kbn-ui-shared-deps/tsconfig.json | 3 +- packages/kbn-ui-shared-deps/webpack.config.js | 11 ++++ .../components/timelion_expression_input.tsx | 10 ++-- .../timelion_expression_input_helpers.ts | 21 ++++---- .../public/code_editor/code_editor.test.tsx | 43 +++++++-------- .../public/code_editor/code_editor.tsx | 52 +++++++++---------- .../public/code_editor/editor_theme.ts | 4 +- .../expression_input.examples.tsx | 2 +- .../expression_input/expression_input.tsx | 31 +++++------ .../canvas/public/lib/monaco_language_def.ts | 2 +- yarn.lock | 14 +---- 16 files changed, 129 insertions(+), 104 deletions(-) create mode 100644 packages/kbn-ui-shared-deps/monaco.ts diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index 48777f1d54aafd..9798391d47da45 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -17,6 +17,7 @@ * under the License. */ +// cache buster - https://github.com/elastic/kibana/issues/58077 - 1 export { OptimizerConfig } from './optimizer'; export * from './run_optimizer'; export * from './log_optimizer_state'; diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index 9a5fb479276f70..5028c6efdb40eb 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -40,6 +40,8 @@ export const ReactDom = require('react-dom'); export const ReactIntl = require('react-intl'); export const ReactRouter = require('react-router'); // eslint-disable-line export const ReactRouterDom = require('react-router-dom'); +export const Monaco = require('./monaco.ts'); +export const MonacoBare = require('monaco-editor/esm/vs/editor/editor.api'); // load timezone data into moment-timezone Moment.tz.load(require('moment-timezone/data/packed/latest.json')); diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js index 5f5ac3f1c9c2ff..c7c004bd55794d 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/index.js @@ -41,4 +41,7 @@ exports.externals = { 'react-intl': '__kbnSharedDeps__.ReactIntl', 'react-router': '__kbnSharedDeps__.ReactRouter', 'react-router-dom': '__kbnSharedDeps__.ReactRouterDom', + '@kbn/ui-shared-deps/monaco': '__kbnSharedDeps__.Monaco', + // this is how plugins/consumers from npm load monaco + 'monaco-editor/esm/vs/editor/editor.api': '__kbnSharedDeps__.MonacoBare', }; diff --git a/packages/kbn-ui-shared-deps/monaco.ts b/packages/kbn-ui-shared-deps/monaco.ts new file mode 100644 index 00000000000000..570aca86c484c5 --- /dev/null +++ b/packages/kbn-ui-shared-deps/monaco.ts @@ -0,0 +1,32 @@ +/* + * 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 * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; + +import 'monaco-editor/esm/vs/base/common/worker/simpleWorker'; +import 'monaco-editor/esm/vs/base/worker/defaultWorkerFactory'; + +import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js'; +import 'monaco-editor/esm/vs/editor/browser/widget/codeEditorWidget.js'; + +import 'monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js'; // Needed for suggestions +import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js'; // Needed for hover +import 'monaco-editor/esm/vs/editor/contrib/parameterHints/parameterHints.js'; // Needed for signature + +export { monaco }; diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 37eba706fe18b2..9f2bf8558062bf 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -11,11 +11,11 @@ "devDependencies": { "abortcontroller-polyfill": "^1.4.0", "@elastic/eui": "19.0.0", + "@kbn/babel-preset": "1.0.0", "@elastic/charts": "^16.1.0", "@kbn/dev-utils": "1.0.0", "@kbn/i18n": "1.0.0", "@yarnpkg/lockfile": "^1.1.0", - "abortcontroller-polyfill": "^1.3.0", "angular": "^1.7.9", "core-js": "^3.2.1", "css-loader": "^3.4.2", diff --git a/packages/kbn-ui-shared-deps/tsconfig.json b/packages/kbn-ui-shared-deps/tsconfig.json index c5c3cba147fcfb..5d981c73f1d211 100644 --- a/packages/kbn-ui-shared-deps/tsconfig.json +++ b/packages/kbn-ui-shared-deps/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "include": [ - "index.d.ts" + "index.d.ts", + "monaco.ts" ] } diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index 87cca2cc897f84..dc6e7ae33dbecd 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -59,6 +59,17 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, + { + include: [require.resolve('./monaco.ts')], + use: [ + { + loader: 'babel-loader', + options: { + presets: [require.resolve('@kbn/babel-preset/webpack_preset')], + }, + }, + ], + }, ], }, diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx index e0aa7f6dad2fa9..c317451b8201e4 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx +++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx @@ -20,7 +20,7 @@ import React, { useEffect, useCallback, useRef, useMemo } from 'react'; import { EuiFormLabel } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; +import { monaco } from '@kbn/ui-shared-deps/monaco'; import { CodeEditor, useKibana } from '../../../../../plugins/kibana_react/public'; import { suggest, getSuggestion } from './timelion_expression_input_helpers'; @@ -31,7 +31,7 @@ import { } from '../../../../../plugins/timelion/common/types'; const LANGUAGE_ID = 'timelion_expression'; -monacoEditor.languages.register({ id: LANGUAGE_ID }); +monaco.languages.register({ id: LANGUAGE_ID }); interface TimelionExpressionInputProps { value: string; @@ -44,10 +44,10 @@ function TimelionExpressionInput({ value, setValue }: TimelionExpressionInputPro const argValueSuggestions = useMemo(getArgValueSuggestions, []); const provideCompletionItems = useCallback( - async (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => { + async (model: monaco.editor.ITextModel, position: monaco.Position) => { const text = model.getValue(); const wordUntil = model.getWordUntilPosition(position); - const wordRange = new monacoEditor.Range( + const wordRange = new monaco.Range( position.lineNumber, wordUntil.startColumn, position.lineNumber, @@ -75,7 +75,7 @@ function TimelionExpressionInput({ value, setValue }: TimelionExpressionInputPro ); const provideHover = useCallback( - async (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => { + async (model: monaco.editor.ITextModel, position: monaco.Position) => { const suggestions = await suggest( model.getValue(), functionList.current, diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts index 93b6a0d463c01b..6f23c864419eb5 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts +++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts @@ -19,7 +19,7 @@ import { get, startsWith } from 'lodash'; import { i18n } from '@kbn/i18n'; -import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; +import { monaco } from '@kbn/ui-shared-deps/monaco'; import { Parser } from 'pegjs'; @@ -215,14 +215,13 @@ export async function suggest( export function getSuggestion( suggestion: ITimelionFunction | TimelionFunctionArgs, type: SUGGESTION_TYPE, - range: monacoEditor.Range -): monacoEditor.languages.CompletionItem { - let kind: monacoEditor.languages.CompletionItemKind = - monacoEditor.languages.CompletionItemKind.Method; + range: monaco.Range +): monaco.languages.CompletionItem { + let kind: monaco.languages.CompletionItemKind = monaco.languages.CompletionItemKind.Method; let insertText: string = suggestion.name; - let insertTextRules: monacoEditor.languages.CompletionItem['insertTextRules']; + let insertTextRules: monaco.languages.CompletionItem['insertTextRules']; let detail: string = ''; - let command: monacoEditor.languages.CompletionItem['command']; + let command: monaco.languages.CompletionItem['command']; switch (type) { case SUGGESTION_TYPE.ARGUMENTS: @@ -230,7 +229,7 @@ export function getSuggestion( title: 'Trigger Suggestion Dialog', id: 'editor.action.triggerSuggest', }; - kind = monacoEditor.languages.CompletionItemKind.Property; + kind = monaco.languages.CompletionItemKind.Property; insertText = `${insertText}=`; detail = `${i18n.translate( 'timelion.expressionSuggestions.argument.description.acceptsText', @@ -245,9 +244,9 @@ export function getSuggestion( title: 'Trigger Suggestion Dialog', id: 'editor.action.triggerSuggest', }; - kind = monacoEditor.languages.CompletionItemKind.Function; + kind = monaco.languages.CompletionItemKind.Function; insertText = `${insertText}($0)`; - insertTextRules = monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet; + insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet; detail = `(${ (suggestion as ITimelionFunction).chainable ? i18n.translate('timelion.expressionSuggestions.func.description.chainableHelpText', { @@ -270,7 +269,7 @@ export function getSuggestion( title: 'Trigger Suggestion Dialog', id: 'editor.action.triggerSuggest', }; - kind = monacoEditor.languages.CompletionItemKind.Property; + kind = monaco.languages.CompletionItemKind.Property; detail = suggestion.help || ''; break; diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx index 2c305c4ea97da3..bcb46fac368561 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx @@ -19,13 +19,13 @@ import React from 'react'; import { CodeEditor } from './code_editor'; -import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; +import { monaco } from '@kbn/ui-shared-deps/monaco'; import { shallow } from 'enzyme'; import 'monaco-editor/esm/vs/basic-languages/html/html.contribution.js'; // A sample language definition with a few example tokens -const simpleLogLang: monacoEditor.languages.IMonarchLanguage = { +const simpleLogLang: monaco.languages.IMonarchLanguage = { tokenizer: { root: [ [/\[error.*/, 'constant'], @@ -36,8 +36,8 @@ const simpleLogLang: monacoEditor.languages.IMonarchLanguage = { }, }; -monacoEditor.languages.register({ id: 'loglang' }); -monacoEditor.languages.setMonarchTokensProvider('loglang', simpleLogLang); +monaco.languages.register({ id: 'loglang' }); +monaco.languages.setMonarchTokensProvider('loglang', simpleLogLang); const logs = ` [Sun Mar 7 20:54:27 2004] [notice] [client xx.xx.xx.xx] This is a notice! @@ -55,23 +55,22 @@ test('is rendered', () => { test('editor mount setup', () => { const suggestionProvider = { - provideCompletionItems: ( - model: monacoEditor.editor.ITextModel, - position: monacoEditor.Position - ) => ({ suggestions: [] }), + provideCompletionItems: (model: monaco.editor.ITextModel, position: monaco.Position) => ({ + suggestions: [], + }), }; const signatureProvider = { provideSignatureHelp: () => ({ signatures: [], activeParameter: 0, activeSignature: 0 }), }; const hoverProvider = { - provideHover: (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => ({ + provideHover: (model: monaco.editor.ITextModel, position: monaco.Position) => ({ contents: [], }), }; const editorWillMount = jest.fn(); - monacoEditor.languages.onLanguage = jest.fn((languageId, func) => { + monaco.languages.onLanguage = jest.fn((languageId, func) => { expect(languageId).toBe('loglang'); // Call the function immediately so we can see our providers @@ -79,11 +78,11 @@ test('editor mount setup', () => { func(); }) as any; - monacoEditor.languages.registerCompletionItemProvider = jest.fn(); - monacoEditor.languages.registerSignatureHelpProvider = jest.fn(); - monacoEditor.languages.registerHoverProvider = jest.fn(); + monaco.languages.registerCompletionItemProvider = jest.fn(); + monaco.languages.registerSignatureHelpProvider = jest.fn(); + monaco.languages.registerHoverProvider = jest.fn(); - monacoEditor.editor.defineTheme = jest.fn(); + monaco.editor.defineTheme = jest.fn(); const wrapper = shallow( { ); const instance = wrapper.instance() as CodeEditor; - instance._editorWillMount(monacoEditor); + instance._editorWillMount(monaco); // Verify our mount callback will be called expect(editorWillMount.mock.calls.length).toBe(1); // Verify our theme will be setup - expect((monacoEditor.editor.defineTheme as jest.Mock).mock.calls.length).toBe(1); + expect((monaco.editor.defineTheme as jest.Mock).mock.calls.length).toBe(1); // Verify our language features have been registered - expect((monacoEditor.languages.onLanguage as jest.Mock).mock.calls.length).toBe(1); - expect( - (monacoEditor.languages.registerCompletionItemProvider as jest.Mock).mock.calls.length - ).toBe(1); - expect( - (monacoEditor.languages.registerSignatureHelpProvider as jest.Mock).mock.calls.length - ).toBe(1); - expect((monacoEditor.languages.registerHoverProvider as jest.Mock).mock.calls.length).toBe(1); + expect((monaco.languages.onLanguage as jest.Mock).mock.calls.length).toBe(1); + expect((monaco.languages.registerCompletionItemProvider as jest.Mock).mock.calls.length).toBe(1); + expect((monaco.languages.registerSignatureHelpProvider as jest.Mock).mock.calls.length).toBe(1); + expect((monaco.languages.registerHoverProvider as jest.Mock).mock.calls.length).toBe(1); }); diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index 62440f12c6d849..37707fdec21403 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -19,18 +19,9 @@ import React from 'react'; import ReactResizeDetector from 'react-resize-detector'; -import MonacoEditor, { EditorDidMount, EditorWillMount } from 'react-monaco-editor'; +import MonacoEditor from 'react-monaco-editor'; -import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; -import 'monaco-editor/esm/vs/base/common/worker/simpleWorker'; -import 'monaco-editor/esm/vs/base/worker/defaultWorkerFactory'; - -import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js'; -import 'monaco-editor/esm/vs/editor/browser/widget/codeEditorWidget.js'; - -import 'monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js'; // Needed for suggestions -import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js'; // Needed for hover -import 'monaco-editor/esm/vs/editor/contrib/parameterHints/parameterHints.js'; // Needed for signature +import { monaco } from '@kbn/ui-shared-deps/monaco'; import { LIGHT_THEME, DARK_THEME } from './editor_theme'; @@ -55,50 +46,50 @@ export interface Props { * Documentation of options can be found here: * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.ieditorconstructionoptions.html */ - options?: monacoEditor.editor.IEditorConstructionOptions; + options?: monaco.editor.IEditorConstructionOptions; /** * Suggestion provider for autocompletion * Documentation for the provider can be found here: * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.completionitemprovider.html */ - suggestionProvider?: monacoEditor.languages.CompletionItemProvider; + suggestionProvider?: monaco.languages.CompletionItemProvider; /** * Signature provider for function parameter info * Documentation for the provider can be found here: * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.signaturehelpprovider.html */ - signatureProvider?: monacoEditor.languages.SignatureHelpProvider; + signatureProvider?: monaco.languages.SignatureHelpProvider; /** * Hover provider for hover documentation * Documentation for the provider can be found here: * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.hoverprovider.html */ - hoverProvider?: monacoEditor.languages.HoverProvider; + hoverProvider?: monaco.languages.HoverProvider; /** * Language config provider for bracket * Documentation for the provider can be found here: * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.languageconfiguration.html */ - languageConfiguration?: monacoEditor.languages.LanguageConfiguration; + languageConfiguration?: monaco.languages.LanguageConfiguration; /** * Function called before the editor is mounted in the view */ - editorWillMount?: EditorWillMount; + editorWillMount?: () => void; /** * Function called before the editor is mounted in the view * and completely replaces the setup behavior called by the component */ - overrideEditorWillMount?: EditorWillMount; + overrideEditorWillMount?: () => void; /** * Function called after the editor is mounted in the view */ - editorDidMount?: EditorDidMount; + editorDidMount?: (editor: monaco.editor.IStandaloneCodeEditor) => void; /** * Should the editor use the dark theme @@ -107,16 +98,20 @@ export interface Props { } export class CodeEditor extends React.Component { - _editor: monacoEditor.editor.IStandaloneCodeEditor | null = null; + _editor: monaco.editor.IStandaloneCodeEditor | null = null; + + _editorWillMount = (__monaco: unknown) => { + if (__monaco !== monaco) { + throw new Error('react-monaco-editor is using a different version of monaco'); + } - _editorWillMount = (monaco: typeof monacoEditor) => { if (this.props.overrideEditorWillMount) { - this.props.overrideEditorWillMount(monaco); + this.props.overrideEditorWillMount(); return; } if (this.props.editorWillMount) { - this.props.editorWillMount(monaco); + this.props.editorWillMount(); } monaco.languages.onLanguage(this.props.languageId, () => { @@ -150,14 +145,15 @@ export class CodeEditor extends React.Component { monaco.editor.defineTheme('euiColors', this.props.useDarkTheme ? DARK_THEME : LIGHT_THEME); }; - _editorDidMount = ( - editor: monacoEditor.editor.IStandaloneCodeEditor, - monaco: typeof monacoEditor - ) => { + _editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor, __monaco: unknown) => { + if (__monaco !== monaco) { + throw new Error('react-monaco-editor is using a different version of monaco'); + } + this._editor = editor; if (this.props.editorDidMount) { - this.props.editorDidMount(editor, monaco); + this.props.editorDidMount(editor); } }; diff --git a/src/plugins/kibana_react/public/code_editor/editor_theme.ts b/src/plugins/kibana_react/public/code_editor/editor_theme.ts index 41702f1b3fc35c..6e30135686797c 100644 --- a/src/plugins/kibana_react/public/code_editor/editor_theme.ts +++ b/src/plugins/kibana_react/public/code_editor/editor_theme.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; +import { monaco } from '@kbn/ui-shared-deps/monaco'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; @@ -27,7 +27,7 @@ import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; export function createTheme( euiTheme: typeof darkTheme | typeof lightTheme, selectionBackgroundColor: string -): monacoEditor.editor.IStandaloneThemeData { +): monaco.editor.IStandaloneThemeData { return { base: 'vs', inherit: true, diff --git a/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx b/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx index 880269385d12fc..51caf1db196bc7 100644 --- a/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx @@ -7,7 +7,7 @@ import { action } from '@storybook/addon-actions'; import { storiesOf } from '@storybook/react'; import React from 'react'; -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import { monaco } from '@kbn/ui-shared-deps/monaco'; import { ExpressionInput } from '../expression_input'; import { language, LANGUAGE_ID } from '../../../lib/monaco_language_def'; diff --git a/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx b/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx index 9653decb6db973..faac2ae883c13f 100644 --- a/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiFormRow } from '@elastic/eui'; import { debounce } from 'lodash'; -import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; +import { monaco } from '@kbn/ui-shared-deps/monaco'; import { ExpressionFunction } from '../../../../../../../src/plugins/expressions'; import { CodeEditor } from '../../../../../../../src/plugins/kibana_react/public'; import { @@ -46,7 +46,7 @@ export class ExpressionInput extends React.Component { onChange: PropTypes.func.isRequired, }; - editor: monacoEditor.editor.IStandaloneCodeEditor | null; + editor: monaco.editor.IStandaloneCodeEditor | null; undoHistory: string[]; redoHistory: string[]; @@ -114,9 +114,9 @@ export class ExpressionInput extends React.Component { }; provideSuggestions = ( - model: monacoEditor.editor.ITextModel, - position: monacoEditor.Position, - context: monacoEditor.languages.CompletionContext + model: monaco.editor.ITextModel, + position: monaco.Position, + context: monaco.languages.CompletionContext ) => { const text = model.getValue(); const textRange = model.getFullModelRange(); @@ -128,13 +128,13 @@ export class ExpressionInput extends React.Component { endColumn: textRange.endColumn, }); - let wordRange: monacoEditor.Range; + let wordRange: monaco.Range; let aSuggestions; if (context.triggerCharacter === '{') { const wordUntil = model.getWordAtPosition(position.delta(0, -3)); if (wordUntil) { - wordRange = new monacoEditor.Range( + wordRange = new monaco.Range( position.lineNumber, position.column, position.lineNumber, @@ -151,7 +151,7 @@ export class ExpressionInput extends React.Component { } } else { const wordUntil = model.getWordUntilPosition(position); - wordRange = new monacoEditor.Range( + wordRange = new monaco.Range( position.lineNumber, wordUntil.startColumn, position.lineNumber, @@ -173,7 +173,7 @@ export class ExpressionInput extends React.Component { if (s.type === 'argument') { return { label: s.argDef.name, - kind: monacoEditor.languages.CompletionItemKind.Variable, + kind: monaco.languages.CompletionItemKind.Variable, documentation: { value: getArgReferenceStr(s.argDef), isTrusted: true }, insertText: s.text, command: { @@ -186,7 +186,7 @@ export class ExpressionInput extends React.Component { } else if (s.type === 'value') { return { label: s.text, - kind: monacoEditor.languages.CompletionItemKind.Value, + kind: monaco.languages.CompletionItemKind.Value, insertText: s.text, command: { title: 'Trigger Suggestion Dialog', @@ -198,7 +198,7 @@ export class ExpressionInput extends React.Component { } else { return { label: s.fnDef.name, - kind: monacoEditor.languages.CompletionItemKind.Function, + kind: monaco.languages.CompletionItemKind.Function, documentation: { value: getFunctionReferenceStr(s.fnDef), isTrusted: true, @@ -219,7 +219,7 @@ export class ExpressionInput extends React.Component { }; }; - providerHover = (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => { + providerHover = (model: monaco.editor.ITextModel, position: monaco.Position) => { const text = model.getValue(); const word = model.getWordAtPosition(position); @@ -248,7 +248,7 @@ export class ExpressionInput extends React.Component { const startPos = model.getPositionAt(argStart); const endPos = model.getPositionAt(argEnd); - const argRange = new monacoEditor.Range( + const argRange = new monaco.Range( startPos.lineNumber, startPos.column, endPos.lineNumber, @@ -275,10 +275,7 @@ export class ExpressionInput extends React.Component { }; }; - editorDidMount = ( - editor: monacoEditor.editor.IStandaloneCodeEditor, - monaco: typeof monacoEditor - ) => { + editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor) => { // Updating tab size for the editor const model = editor.getModel(); if (model) { diff --git a/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts b/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts index e15be9a90beb0c..cd054bff3b7d24 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import { monaco } from '@kbn/ui-shared-deps/monaco'; import { npSetup } from 'ui/new_platform'; export const LANGUAGE_ID = 'canvas-expression'; diff --git a/yarn.lock b/yarn.lock index 7361fe0e74d838..59b857b12dd0b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6442,11 +6442,6 @@ abort-controller@^2.0.3: dependencies: event-target-shim "^5.0.0" -abortcontroller-polyfill@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.3.0.tgz#de69af32ae926c210b7efbcc29bf644ee4838b00" - integrity sha512-lbWQgf+eRvku3va8poBlDBO12FigTQr9Zb7NIjXrePrhxWVKdCP2wbDl1tLDaYa18PWTom3UEWwdH13S46I+yA== - abortcontroller-polyfill@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.4.0.tgz#0d5eb58e522a461774af8086414f68e1dda7a6c4" @@ -10618,7 +10613,7 @@ contour_plot@^0.0.1: resolved "https://registry.yarnpkg.com/contour_plot/-/contour_plot-0.0.1.tgz#475870f032b8e338412aa5fc507880f0bf495c77" integrity sha1-R1hw8DK44zhBKqX8UHiA8L9JXHc= -convert-source-map@1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -10642,13 +10637,6 @@ convert-source-map@^1.5.1, convert-source-map@^1.6.0: dependencies: safe-buffer "~5.1.1" -convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - convex-hull@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/convex-hull/-/convex-hull-1.0.3.tgz#20a3aa6ce87f4adea2ff7d17971c9fc1c67e1fff" From 7c371a20f3e6316c82df647ff6764c8b2488fc66 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 20 Feb 2020 18:57:12 -0500 Subject: [PATCH 10/19] [ML] New Platform server shim: update datafeed routes (#57739) (#58174) * convert datafeed routes to new platfrom * update datafeed schema * consolidate datafeedConfig schema for datafeed + job validation --- .../server/new_platform/datafeeds_schema.ts | 31 ++ .../new_platform/job_validation_schema.ts | 17 +- .../plugins/ml/server/new_platform/plugin.ts | 1 - .../plugins/ml/server/routes/apidoc.json | 13 +- .../plugins/ml/server/routes/datafeeds.js | 152 --------- .../plugins/ml/server/routes/datafeeds.ts | 320 ++++++++++++++++++ 6 files changed, 364 insertions(+), 170 deletions(-) create mode 100644 x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts delete mode 100644 x-pack/legacy/plugins/ml/server/routes/datafeeds.js create mode 100644 x-pack/legacy/plugins/ml/server/routes/datafeeds.ts diff --git a/x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts new file mode 100644 index 00000000000000..02677dcb107c2f --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts @@ -0,0 +1,31 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +export const startDatafeedSchema = schema.object({ + start: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + end: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + timeout: schema.maybe(schema.any()), +}); + +export const datafeedConfigSchema = schema.object({ + datafeed_id: schema.maybe(schema.string()), + feed_id: schema.maybe(schema.string()), + aggregations: schema.maybe(schema.any()), + aggs: schema.maybe(schema.any()), + chunking_config: schema.maybe(schema.any()), + frequency: schema.maybe(schema.string()), + indices: schema.arrayOf(schema.string()), + indexes: schema.maybe(schema.arrayOf(schema.string())), + job_id: schema.maybe(schema.string()), + query: schema.maybe(schema.any()), + max_empty_searches: schema.maybe(schema.number()), + query_delay: schema.maybe(schema.string()), + script_fields: schema.maybe(schema.any()), + scroll_size: schema.maybe(schema.number()), + delayed_data_check_config: schema.maybe(schema.any()), +}); diff --git a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts index 5917ec50884d80..5da825a905e8da 100644 --- a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts +++ b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts @@ -6,6 +6,7 @@ import { schema } from '@kbn/config-schema'; import { anomalyDetectionJobSchema } from './anomaly_detectors_schema'; +import { datafeedConfigSchema } from './datafeeds_schema'; export const estimateBucketSpanSchema = schema.object({ aggTypes: schema.arrayOf(schema.nullable(schema.string())), @@ -38,22 +39,6 @@ export const validateJobSchema = schema.object({ job: schema.object(anomalyDetectionJobSchema), }); -const datafeedConfigSchema = schema.object({ - datafeed_id: schema.string(), - aggregations: schema.maybe(schema.any()), - aggs: schema.maybe(schema.any()), - chunking_config: schema.maybe(schema.any()), - frequency: schema.maybe(schema.string()), - indices: schema.arrayOf(schema.string()), - indexes: schema.maybe(schema.arrayOf(schema.string())), - job_id: schema.string(), - query: schema.any(), - query_delay: schema.maybe(schema.string()), - script_fields: schema.maybe(schema.any()), - scroll_size: schema.maybe(schema.number()), - delayed_data_check_config: schema.maybe(schema.any()), -}); - export const validateCardinalitySchema = { ...anomalyDetectionJobSchema, datafeed_config: datafeedConfigSchema, 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 e006ad3d3718f1..10961182be841a 100644 --- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts +++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts @@ -27,7 +27,6 @@ import { mirrorPluginStatus } from '../../../../server/lib/mirror_plugin_status' import { LICENSE_TYPE } from '../../common/constants/license'; import { annotationRoutes } from '../routes/annotations'; import { jobRoutes } from '../routes/anomaly_detectors'; -// @ts-ignore: could not find declaration file for module import { dataFeedRoutes } from '../routes/datafeeds'; // @ts-ignore: could not find declaration file for module import { indicesRoutes } from '../routes/indices'; diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json index 89751abdbe20dc..7d1f13ead3fef5 100644 --- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json +++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json @@ -94,6 +94,17 @@ "ValidateCardinality", "ValidateJob", "NotificationSettings", - "GetNotificationSettings" + "GetNotificationSettings", + "DatafeedService", + "GetDatafeeds", + "GetDatafeed", + "GetDatafeedsStats", + "GetDatafeedStats", + "CreateDatafeed", + "UpdateDatafeed", + "DeleteDatafeed", + "StartDatafeed", + "StopDatafeed", + "PreviewDatafeed" ] } diff --git a/x-pack/legacy/plugins/ml/server/routes/datafeeds.js b/x-pack/legacy/plugins/ml/server/routes/datafeeds.js deleted file mode 100644 index daa83795ff7d23..00000000000000 --- a/x-pack/legacy/plugins/ml/server/routes/datafeeds.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { callWithRequestFactory } from '../client/call_with_request_factory'; -import { wrapError } from '../client/errors'; - -export function dataFeedRoutes({ commonRouteConfig, elasticsearchPlugin, route }) { - route({ - method: 'GET', - path: '/api/ml/datafeeds', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - return callWithRequest('ml.datafeeds').catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'GET', - path: '/api/ml/datafeeds/{datafeedId}', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const datafeedId = request.params.datafeedId; - return callWithRequest('ml.datafeeds', { datafeedId }).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'GET', - path: '/api/ml/datafeeds/_stats', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - return callWithRequest('ml.datafeedStats').catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'GET', - path: '/api/ml/datafeeds/{datafeedId}/_stats', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const datafeedId = request.params.datafeedId; - return callWithRequest('ml.datafeedStats', { datafeedId }).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'PUT', - path: '/api/ml/datafeeds/{datafeedId}', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const datafeedId = request.params.datafeedId; - const body = request.payload; - return callWithRequest('ml.addDatafeed', { datafeedId, body }).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/datafeeds/{datafeedId}/_update', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const datafeedId = request.params.datafeedId; - const body = request.payload; - return callWithRequest('ml.updateDatafeed', { datafeedId, body }).catch(resp => - wrapError(resp) - ); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'DELETE', - path: '/api/ml/datafeeds/{datafeedId}', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const options = { - datafeedId: request.params.datafeedId, - }; - const force = request.query.force; - if (force !== undefined) { - options.force = force; - } - return callWithRequest('ml.deleteDatafeed', options).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/datafeeds/{datafeedId}/_start', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const datafeedId = request.params.datafeedId; - const start = request.payload.start; - const end = request.payload.end; - return callWithRequest('ml.startDatafeed', { datafeedId, start, end }).catch(resp => - wrapError(resp) - ); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/datafeeds/{datafeedId}/_stop', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const datafeedId = request.params.datafeedId; - return callWithRequest('ml.stopDatafeed', { datafeedId }).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'GET', - path: '/api/ml/datafeeds/{datafeedId}/_preview', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const datafeedId = request.params.datafeedId; - return callWithRequest('ml.datafeedPreview', { datafeedId }).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); -} diff --git a/x-pack/legacy/plugins/ml/server/routes/datafeeds.ts b/x-pack/legacy/plugins/ml/server/routes/datafeeds.ts new file mode 100644 index 00000000000000..9335403616cf7d --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/routes/datafeeds.ts @@ -0,0 +1,320 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory'; +import { wrapError } from '../client/error_wrapper'; +import { RouteInitialization } from '../new_platform/plugin'; +import { startDatafeedSchema, datafeedConfigSchema } from '../new_platform/datafeeds_schema'; + +/** + * Routes for datafeed service + */ +export function dataFeedRoutes({ xpackMainPlugin, router }: RouteInitialization) { + /** + * @apiGroup DatafeedService + * + * @api {get} /api/ml/datafeeds Get all datafeeds + * @apiName GetDatafeeds + * @apiDescription Retrieves configuration information for datafeeds + */ + router.get( + { + path: '/api/ml/datafeeds', + validate: false, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeeds'); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {get} /api/ml/datafeeds/:datafeedId Get datafeed for given datafeed id + * @apiName GetDatafeed + * @apiDescription Retrieves configuration information for datafeed + */ + router.get( + { + path: '/api/ml/datafeeds/{datafeedId}', + validate: { + params: schema.object({ datafeedId: schema.string() }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const datafeedId = request.params.datafeedId; + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeeds', { datafeedId }); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {get} /api/ml/datafeeds/_stats Get stats for all datafeeds + * @apiName GetDatafeedsStats + * @apiDescription Retrieves usage information for datafeeds + */ + router.get( + { + path: '/api/ml/datafeeds/_stats', + validate: false, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeedStats'); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {get} /api/ml/datafeeds/:datafeedId/_stats Get datafeed stats for given datafeed id + * @apiName GetDatafeedStats + * @apiDescription Retrieves usage information for datafeed + */ + router.get( + { + path: '/api/ml/datafeeds/{datafeedId}/_stats', + validate: { + params: schema.object({ datafeedId: schema.string() }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const datafeedId = request.params.datafeedId; + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeedStats', { + datafeedId, + }); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {put} /api/ml/datafeeds/:datafeedId Creates datafeed + * @apiName CreateDatafeed + * @apiDescription Instantiates a datafeed + */ + router.put( + { + path: '/api/ml/datafeeds/{datafeedId}', + validate: { + params: schema.object({ datafeedId: schema.string() }), + body: datafeedConfigSchema, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const datafeedId = request.params.datafeedId; + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.addDatafeed', { + datafeedId, + body: request.body, + }); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {post} /api/ml/datafeeds/:datafeedId/_update Updates datafeed for given datafeed id + * @apiName UpdateDatafeed + * @apiDescription Updates certain properties of a datafeed + */ + router.post( + { + path: '/api/ml/datafeeds/{datafeedId}/_update', + validate: { + params: schema.object({ datafeedId: schema.string() }), + body: datafeedConfigSchema, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const datafeedId = request.params.datafeedId; + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.updateDatafeed', { + datafeedId, + body: request.body, + }); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {delete} /api/ml/datafeeds/:datafeedId Deletes datafeed + * @apiName DeleteDatafeed + * @apiDescription Deletes an existing datafeed + */ + router.delete( + { + path: '/api/ml/datafeeds/{datafeedId}', + validate: { + params: schema.object({ datafeedId: schema.string() }), + query: schema.maybe(schema.object({ force: schema.maybe(schema.any()) })), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const options: { datafeedId: string; force?: boolean } = { + datafeedId: request.params.jobId, + }; + const force = request.query.force; + if (force !== undefined) { + options.force = force; + } + + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.deleteDatafeed', options); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {post} /api/ml/datafeeds/:datafeedId/_start Starts datafeed for given datafeed id(s) + * @apiName StartDatafeed + * @apiDescription Starts one or more datafeeds + */ + router.post( + { + path: '/api/ml/datafeeds/{datafeedId}/_start', + validate: { + params: schema.object({ datafeedId: schema.string() }), + body: startDatafeedSchema, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const datafeedId = request.params.datafeedId; + const { start, end } = request.body; + + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.startDatafeed', { + datafeedId, + start, + end, + }); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {post} /api/ml/datafeeds/:datafeedId/_stop Stops datafeed for given datafeed id(s) + * @apiName StopDatafeed + * @apiDescription Stops one or more datafeeds + */ + router.post( + { + path: '/api/ml/datafeeds/{datafeedId}/_stop', + validate: { + params: schema.object({ datafeedId: schema.string() }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const datafeedId = request.params.datafeedId; + + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.stopDatafeed', { + datafeedId, + }); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup DatafeedService + * + * @api {get} /api/ml/datafeeds/:datafeedId/_preview Preview datafeed for given datafeed id + * @apiName PreviewDatafeed + * @apiDescription Previews a datafeed + */ + router.get( + { + path: '/api/ml/datafeeds/{datafeedId}/_preview', + validate: { + params: schema.object({ datafeedId: schema.string() }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const datafeedId = request.params.datafeedId; + const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeedPreview', { + datafeedId, + }); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); +} From ef0676bab4c3a44f8cf55ff3603e91712df28236 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Thu, 20 Feb 2020 18:02:52 -0700 Subject: [PATCH 11/19] [SIEM][Detection Engine] Fixes return codes where some were rule_id instead of id (#58191) ## Summary Fixes some return error codes where they were `rule_id` when they should have been `id` - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios --- .../routes/rules/import_rules_route.test.ts | 4 +- .../routes/rules/import_rules_route.ts | 1 - .../routes/rules/utils.test.ts | 14 ++++- .../detection_engine/routes/rules/utils.ts | 12 +++-- .../lib/detection_engine/routes/utils.ts | 51 +++++++++++++++---- .../tests/delete_rules_bulk.ts | 8 +-- .../tests/patch_rules_bulk.ts | 4 +- .../tests/update_rules_bulk.ts | 4 +- 8 files changed, 72 insertions(+), 26 deletions(-) diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index e3283a750869c5..b1dd08f8ca371b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -210,7 +210,7 @@ describe('import_rules_route', () => { message: 'Unexpected token : in JSON at position 8', status_code: 400, }, - rule_id: '(unknown)', + rule_id: '(unknown id)', }, ], success: false, @@ -329,7 +329,7 @@ describe('import_rules_route', () => { message: 'Unexpected token : in JSON at position 8', status_code: 400, }, - rule_id: '(unknown)', + rule_id: '(unknown id)', }, ], success: false, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index cdb09ff7b04eda..f438e0120f96ac 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -95,7 +95,6 @@ export const createImportRulesRoute = ( // early with the error and an (unknown) for the ruleId resolve( createBulkErrorObject({ - ruleId: '(unknown)', statusCode: 400, message: parsedRule.message, }) diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts index 2b0da8251b387c..593c55bcae9f21 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts @@ -794,10 +794,20 @@ describe('utils', () => { }); describe('getIdBulkError', () => { + test('outputs message about id and rule_id not being found if both are not null', () => { + const error = getIdBulkError({ id: '123', ruleId: '456' }); + const expected: BulkError = { + id: '123', + rule_id: '456', + error: { message: 'id: "123" and rule_id: "456" not found', status_code: 404 }, + }; + expect(error).toEqual(expected); + }); + test('outputs message about id not being found if only id is defined and ruleId is undefined', () => { const error = getIdBulkError({ id: '123', ruleId: undefined }); const expected: BulkError = { - rule_id: '123', + id: '123', error: { message: 'id: "123" not found', status_code: 404 }, }; expect(error).toEqual(expected); @@ -806,7 +816,7 @@ describe('utils', () => { test('outputs message about id not being found if only id is defined and ruleId is null', () => { const error = getIdBulkError({ id: '123', ruleId: null }); const expected: BulkError = { - rule_id: '123', + id: '123', error: { message: 'id: "123" not found', status_code: 404 }, }; expect(error).toEqual(expected); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts index 21fc5a12db536d..198cdbfb9771d7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts @@ -62,9 +62,16 @@ export const getIdBulkError = ({ id: string | undefined | null; ruleId: string | undefined | null; }): BulkError => { - if (id != null) { + if (id != null && ruleId != null) { + return createBulkErrorObject({ + id, + ruleId, + statusCode: 404, + message: `id: "${id}" and rule_id: "${ruleId}" not found`, + }); + } else if (id != null) { return createBulkErrorObject({ - ruleId: id, + id, statusCode: 404, message: `id: "${id}" not found`, }); @@ -76,7 +83,6 @@ export const getIdBulkError = ({ }); } else { return createBulkErrorObject({ - ruleId: '(unknown id)', statusCode: 404, message: `id or rule_id should have been defined`, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts index 4a586e21c9e7f4..55832ab67dc6ba 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts @@ -44,7 +44,8 @@ export const transformError = (err: Error & { statusCode?: number }): OutputErro }; export interface BulkError { - rule_id: string; + id?: string; + rule_id?: string; error: { status_code: number; message: string; @@ -53,24 +54,54 @@ export interface BulkError { export const createBulkErrorObject = ({ ruleId, + id, statusCode, message, }: { - ruleId: string; + ruleId?: string; + id?: string; statusCode: number; message: string; }): BulkError => { - return { - rule_id: ruleId, - error: { - status_code: statusCode, - message, - }, - }; + if (id != null && ruleId != null) { + return { + id, + rule_id: ruleId, + error: { + status_code: statusCode, + message, + }, + }; + } else if (id != null) { + return { + id, + error: { + status_code: statusCode, + message, + }, + }; + } else if (ruleId != null) { + return { + rule_id: ruleId, + error: { + status_code: statusCode, + message, + }, + }; + } else { + return { + rule_id: '(unknown id)', + error: { + status_code: statusCode, + message, + }, + }; + } }; export interface ImportRuleResponse { - rule_id: string; + rule_id?: string; + id?: string; status_code?: number; message?: string; error?: { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts index 5a1c178f6b2110..6b87c940291891 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts @@ -129,7 +129,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'id: "fake_id" not found', status_code: 404, }, - rule_id: 'fake_id', // TODO This is a known issue where it should be id and not rule_id + id: 'fake_id', }, ]); }); @@ -152,7 +152,7 @@ export default ({ getService }: FtrProviderContext): void => { const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body[0]); expect([bodyToCompare, body[1]]).to.eql([ getSimpleRuleOutputWithoutRuleId(), - { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, + { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, ]); }); }); @@ -262,7 +262,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'id: "fake_id" not found', status_code: 404, }, - rule_id: 'fake_id', // TODO This is a known issue where it should be id and not rule_id + id: 'fake_id', }, ]); }); @@ -285,7 +285,7 @@ export default ({ getService }: FtrProviderContext): void => { const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body[0]); expect([bodyToCompare, body[1]]).to.eql([ getSimpleRuleOutputWithoutRuleId(), - { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, + { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, ]); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts index 3d14bc2db47b44..c13e8909dacf90 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts @@ -263,7 +263,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); expect(body).to.eql([ - { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, + { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, ]); }); @@ -347,7 +347,7 @@ export default ({ getService }: FtrProviderContext) => { message: 'id: "fake_id" not found', status_code: 404, }, - rule_id: 'fake_id', // TODO: This should be id and not rule_id in the codebase + id: 'fake_id', }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts index 4894cac2b2608c..220a4af4c5c5e4 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts @@ -277,7 +277,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); expect(body).to.eql([ - { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, + { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } }, ]); }); @@ -377,7 +377,7 @@ export default ({ getService }: FtrProviderContext) => { message: 'id: "fake_id" not found', status_code: 404, }, - rule_id: 'fake_id', // TODO: This should be id and not rule_id in the codebase + id: 'fake_id', }, ]); }); From 916aa7ba93608db8c7b57560144d129692b9afb7 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 20 Feb 2020 18:11:30 -0800 Subject: [PATCH 12/19] Add filter for ILM phase to Index Management (revert #45486) (#57402) (#58145) Co-authored-by: Elastic Machine --- .../extend_index_management.test.js.snap | 24 +++++++++++++ .../np_ready/extend_index_management/index.js | 34 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap index 92aaa171551a0d..74c3e7408fe7cd 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap +++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap @@ -63,6 +63,30 @@ Array [ ], "type": "field_value_selection", }, + Object { + "field": "ilm.phase", + "multiSelect": "or", + "name": "Lifecycle phase", + "options": Array [ + Object { + "value": "hot", + "view": "Hot", + }, + Object { + "value": "warm", + "view": "Warm", + }, + Object { + "value": "cold", + "view": "Cold", + }, + Object { + "value": "delete", + "view": "Delete", + }, + ], + "type": "field_value_selection", + }, ] `; 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 6958c4ecce0cc6..0e662b78b2c180 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 @@ -200,6 +200,40 @@ export const ilmFilterExtension = indices => { }, ], }, + { + type: 'field_value_selection', + field: 'ilm.phase', + name: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.lifecyclePhaseLabel', { + defaultMessage: 'Lifecycle phase', + }), + multiSelect: 'or', + options: [ + { + value: 'hot', + view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.hotLabel', { + defaultMessage: 'Hot', + }), + }, + { + value: 'warm', + view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.warmLabel', { + defaultMessage: 'Warm', + }), + }, + { + value: 'cold', + view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.coldLabel', { + defaultMessage: 'Cold', + }), + }, + { + value: 'delete', + view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.deleteLabel', { + defaultMessage: 'Delete', + }), + }, + ], + }, ]; } }; From b3af3179a21a29a4afb3b744f2f4dc9d9e4061a2 Mon Sep 17 00:00:00 2001 From: Nick Partridge Date: Thu, 20 Feb 2020 22:03:00 -0600 Subject: [PATCH 13/19] Fix legend sizing on area charts (#58083) (#58182) --- .../vislib/components/legend/legend.tsx | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx index c1563625c3b8c2..b9d218b089c31f 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx @@ -53,6 +53,7 @@ export interface VisLegendState { open: boolean; labels: any[]; tableAggs: any[]; + filterableLabels: Set; selectedLabel: string | null; } @@ -68,6 +69,7 @@ export class VisLegend extends PureComponent { open, labels: [], tableAggs: [], + filterableLabels: new Set(), selectedLabel: null, }; } @@ -133,40 +135,43 @@ export class VisLegend extends PureComponent { })); }; - // Most of these functions were moved directly from the old Legend class. Not a fan of this. - setLabels = (data: any, type: string): Promise => + setFilterableLabels = (items: LegendItem[]): Promise => new Promise(async resolve => { - let labels = []; - if (CUSTOM_LEGEND_VIS_TYPES.includes(type)) { - const legendLabels = this.props.vislibVis.getLegendLabels(); - if (legendLabels) { - labels = map(legendLabels, label => { - return { label }; - }); + const filterableLabels = new Set(); + items.forEach(async item => { + const canFilter = await this.canFilter(item); + if (canFilter) { + filterableLabels.add(item.label); } - } else { - if (!data) return []; - data = data.columns || data.rows || [data]; + }); + + this.setState({ filterableLabels }, resolve); + }); - labels = type === 'pie' ? getPieNames(data) : this.getSeriesLabels(data); + setLabels = (data: any, type: string) => { + let labels = []; + if (CUSTOM_LEGEND_VIS_TYPES.includes(type)) { + const legendLabels = this.props.vislibVis.getLegendLabels(); + if (legendLabels) { + labels = map(legendLabels, label => { + return { label }; + }); } + } else { + if (!data) return []; + data = data.columns || data.rows || [data]; - const labelsConfig = await Promise.all( - labels.map(async label => ({ - ...label, - canFilter: await this.canFilter(label), - })) - ); - - this.setState( - { - labels: labelsConfig, - }, - resolve - ); + labels = type === 'pie' ? getPieNames(data) : this.getSeriesLabels(data); + } + + this.setFilterableLabels(labels); + + this.setState({ + labels, }); + }; - refresh = async () => { + refresh = () => { const vislibVis = this.props.vislibVis; if (!vislibVis || !vislibVis.visConfig) { this.setState({ @@ -193,7 +198,7 @@ export class VisLegend extends PureComponent { } this.setState({ tableAggs: getTableAggs(this.props.vis) }); - await this.setLabels(this.props.visData, vislibVis.visConfigArgs.type); + this.setLabels(this.props.visData, vislibVis.visConfigArgs.type); }; highlight = (event: BaseSyntheticEvent) => { @@ -241,7 +246,7 @@ export class VisLegend extends PureComponent { key={item.label} anchorPosition={anchorPosition} selected={this.state.selectedLabel === item.label} - canFilter={item.canFilter} + canFilter={this.state.filterableLabels.has(item.label)} onFilter={this.filter} onSelect={this.toggleDetails} legendId={this.legendId} From d7b70f64792cb77f040fb264b911f16a05730269 Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Fri, 21 Feb 2020 01:43:23 -0500 Subject: [PATCH 14/19] Moved all of the show/hide toggles outside of ordered lists. (#57163) (#58063) * Moved all of the show/hide toggles outside of ordered lists. * Fixed styling issues for indice list. * Fixed i10n tag that I accidentally changed. * Added component to show/hide indices. * Abstracted out some of the common parts of the Show Hide component and implemented the general component in the pages. Also made conditional for the i18n tags. * Fixed changes per comments. Restored to fix the issue with the bullet points. Updated the i18n tags to be more generic. Created 2 components for formatted message. * Fixed internalization issues.. Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../components/collapsible_indices_list.tsx | 81 ++++++++++++++++++ .../public/app/components/index.ts | 1 + .../policy_form/steps/step_review.tsx | 60 +------------- .../steps/step_review.tsx | 59 +------------ .../policy_details/tabs/tab_summary.tsx | 82 +------------------ .../snapshot_details/tabs/tab_summary.tsx | 80 ++---------------- .../translations/translations/ja-JP.json | 12 --- .../translations/translations/zh-CN.json | 12 --- 8 files changed, 98 insertions(+), 289 deletions(-) create mode 100644 x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx new file mode 100644 index 00000000000000..96224ec1283e23 --- /dev/null +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx @@ -0,0 +1,81 @@ +/* + * 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, { useState } from 'react'; +import { EuiTitle, EuiLink, EuiIcon, EuiText, EuiSpacer } from '@elastic/eui'; +interface Props { + indices: string[] | string | undefined; +} + +import { useAppDependencies } from '../index'; + +export const CollapsibleIndicesList: React.FunctionComponent = ({ indices }) => { + const { + core: { i18n }, + } = useAppDependencies(); + const { FormattedMessage } = i18n; + const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState(false); + const displayIndices = indices + ? typeof indices === 'string' + ? indices.split(',') + : indices + : undefined; + const hiddenIndicesCount = + displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0; + return ( + <> + {displayIndices ? ( + <> + +
    + {(isShowingFullIndicesList ? displayIndices : [...displayIndices].splice(0, 10)).map( + index => ( +
  • + + {index} + +
  • + ) + )} +
+
+ {hiddenIndicesCount ? ( + <> + + + isShowingFullIndicesList + ? setIsShowingFullIndicesList(false) + : setIsShowingFullIndicesList(true) + } + > + {isShowingFullIndicesList ? ( + + ) : ( + + )}{' '} + + + + ) : null} + + ) : ( + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts b/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts index 32b45c05d5cb33..a7038ebd715785 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts @@ -16,6 +16,7 @@ export { SnapshotDeleteProvider } from './snapshot_delete_provider'; export { RestoreSnapshotForm } from './restore_snapshot_form'; export { PolicyExecuteProvider } from './policy_execute_provider'; export { PolicyDeleteProvider } from './policy_delete_provider'; +export { CollapsibleIndicesList } from './collapsible_indices_list'; export { RetentionSettingsUpdateModalProvider, UpdateRetentionSettings, diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.tsx index 3ddbcd94009acf..a7f7748b7d72f6 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.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 } from 'react'; +import React, { Fragment } from 'react'; import { EuiCodeBlock, EuiFlexGroup, @@ -13,7 +13,6 @@ import { EuiDescriptionListDescription, EuiSpacer, EuiTabbedContent, - EuiText, EuiTitle, EuiLink, EuiIcon, @@ -22,6 +21,7 @@ import { import { serializePolicy } from '../../../../../common/lib'; import { useAppDependencies } from '../../../index'; import { StepProps } from './'; +import { CollapsibleIndicesList } from '../../collapsible_indices_list'; export const PolicyStepReview: React.FunctionComponent = ({ policy, @@ -39,15 +39,6 @@ export const PolicyStepReview: React.FunctionComponent = ({ partial: undefined, }; - const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState(false); - const displayIndices = indices - ? typeof indices === 'string' - ? indices.split(',') - : indices - : undefined; - const hiddenIndicesCount = - displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0; - const serializedPolicy = serializePolicy(policy); const { retention: serializedRetention } = serializedPolicy; @@ -164,52 +155,7 @@ export const PolicyStepReview: React.FunctionComponent = ({ /> - {displayIndices ? ( - -
    - {(isShowingFullIndicesList - ? displayIndices - : [...displayIndices].splice(0, 10) - ).map(index => ( -
  • - - {index} - -
  • - ))} - {hiddenIndicesCount ? ( -
  • - - {isShowingFullIndicesList ? ( - setIsShowingFullIndicesList(false)}> - {' '} - - - ) : ( - setIsShowingFullIndicesList(true)}> - {' '} - - - )} - -
  • - ) : null} -
-
- ) : ( - - )} +
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.tsx index 92b0ee48fef01e..0d2c2398c6012c 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.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, { useState, Fragment } from 'react'; +import React, { Fragment } from 'react'; import { EuiCodeEditor, EuiFlexGrid, @@ -23,6 +23,7 @@ import { import { serializeRestoreSettings } from '../../../../../common/lib'; import { useAppDependencies } from '../../../index'; import { StepProps } from './'; +import { CollapsibleIndicesList } from '../../collapsible_indices_list'; export const RestoreSnapshotStepReview: React.FunctionComponent = ({ restoreSettings, @@ -44,15 +45,6 @@ export const RestoreSnapshotStepReview: React.FunctionComponent = ({ const serializedRestoreSettings = serializeRestoreSettings(restoreSettings); const { index_settings: serializedIndexSettings } = serializedRestoreSettings; - const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState(false); - const displayIndices = restoreIndices - ? typeof restoreIndices === 'string' - ? restoreIndices.split(',') - : restoreIndices - : undefined; - const hiddenIndicesCount = - displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0; - const renderSummaryTab = () => ( @@ -88,52 +80,7 @@ export const RestoreSnapshotStepReview: React.FunctionComponent = ({ /> - {displayIndices ? ( - -
    - {(isShowingFullIndicesList - ? displayIndices - : [...displayIndices].splice(0, 10) - ).map(index => ( -
  • - - {index} - -
  • - ))} - {hiddenIndicesCount ? ( -
  • - - {isShowingFullIndicesList ? ( - setIsShowingFullIndicesList(false)}> - {' '} - - - ) : ( - setIsShowingFullIndicesList(true)}> - {' '} - - - )} - -
  • - ) : null} -
-
- ) : ( - - )} +
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.tsx index e719f3cd1451b1..1f63115c3a5fbf 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.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, { useState, useEffect, Fragment } from 'react'; +import React, { Fragment } from 'react'; import { EuiCallOut, EuiFlexGroup, @@ -13,8 +13,6 @@ import { EuiDescriptionList, EuiDescriptionListTitle, EuiDescriptionListDescription, - EuiIcon, - EuiText, EuiPanel, EuiStat, EuiSpacer, @@ -23,7 +21,7 @@ import { import { SlmPolicy } from '../../../../../../../common/types'; import { useAppDependencies } from '../../../../../index'; -import { FormattedDateTime } from '../../../../../components'; +import { FormattedDateTime, CollapsibleIndicesList } from '../../../../../components'; import { linkToSnapshots, linkToRepository } from '../../../../../services/navigation'; interface Props { @@ -56,80 +54,6 @@ export const TabSummary: React.FunctionComponent = ({ policy }) => { partial: undefined, }; - // Only show 10 indices initially - const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState(false); - const displayIndices = typeof indices === 'string' ? indices.split(',') : indices; - const hiddenIndicesCount = - displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0; - const shortIndicesList = - displayIndices && displayIndices.length ? ( - -
    - {[...displayIndices].splice(0, 10).map((index: string) => ( -
  • - - {index} - -
  • - ))} - {hiddenIndicesCount ? ( -
  • - - setIsShowingFullIndicesList(true)}> - {' '} - - - -
  • - ) : null} -
-
- ) : ( - - ); - const fullIndicesList = - displayIndices && displayIndices.length && displayIndices.length > 10 ? ( - -
    - {displayIndices.map((index: string) => ( -
  • - - {index} - -
  • - ))} - {hiddenIndicesCount ? ( -
  • - - setIsShowingFullIndicesList(false)}> - {' '} - - - -
  • - ) : null} -
-
- ) : null; - - // Reset indices list state when clicking through different policies - useEffect(() => { - return () => { - setIsShowingFullIndicesList(false); - }; - }, []); - return ( {isManagedPolicy ? ( @@ -314,7 +238,7 @@ export const TabSummary: React.FunctionComponent = ({ policy }) => { - {isShowingFullIndicesList ? fullIndicesList : shortIndicesList} + diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx index d3d32cb1490647..c71fead0a6fc26 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { EuiDescriptionList, @@ -14,15 +14,16 @@ import { EuiFlexItem, EuiLink, EuiLoadingSpinner, - EuiText, - EuiTitle, - EuiIcon, } from '@elastic/eui'; import { SnapshotDetails } from '../../../../../../../common/types'; import { SNAPSHOT_STATE } from '../../../../../constants'; import { useAppDependencies } from '../../../../../index'; -import { DataPlaceholder, FormattedDateTime } from '../../../../../components'; +import { + DataPlaceholder, + FormattedDateTime, + CollapsibleIndicesList, +} from '../../../../../components'; import { linkToPolicy } from '../../../../../services/navigation'; import { SnapshotState } from './snapshot_state'; @@ -52,73 +53,6 @@ export const TabSummary: React.FC = ({ snapshotDetails }) => { policyName, } = snapshotDetails; - // Only show 10 indices initially - const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState(false); - const hiddenIndicesCount = indices.length > 10 ? indices.length - 10 : 0; - const shortIndicesList = indices.length ? ( -
    - {[...indices].splice(0, 10).map((index: string) => ( -
  • - - {index} - -
  • - ))} - {hiddenIndicesCount ? ( -
  • - - setIsShowingFullIndicesList(true)}> - {' '} - - - -
  • - ) : null} -
- ) : ( - - ); - const fullIndicesList = - indices.length && indices.length > 10 ? ( -
    - {indices.map((index: string) => ( -
  • - - {index} - -
  • - ))} - {hiddenIndicesCount ? ( -
  • - - setIsShowingFullIndicesList(false)}> - {' '} - - - -
  • - ) : null} -
- ) : null; - - // Reset indices list state when clicking through different snapshots - useEffect(() => { - return () => { - setIsShowingFullIndicesList(false); - }; - }, []); - return ( @@ -198,7 +132,7 @@ export const TabSummary: React.FC = ({ snapshotDetails }) => { - {isShowingFullIndicesList ? fullIndicesList : shortIndicesList} + diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 958e40125fefc4..117a6fad90d411 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11550,7 +11550,6 @@ "xpack.snapshotRestore.home.snapshotRestoreTitle": "スナップショットリポジドリ", "xpack.snapshotRestore.home.snapshotsTabTitle": "スナップショット", "xpack.snapshotRestore.policies.breadcrumbTitle": "ポリシー", - "xpack.snapshotRestore.policyDetails.allIndicesLabel": "すべてのインデックス", "xpack.snapshotRestore.policyDetails.closeButtonLabel": "閉じる", "xpack.snapshotRestore.policyDetails.deleteButtonLabel": "削除", "xpack.snapshotRestore.policyDetails.editButtonLabel": "編集", @@ -11564,9 +11563,7 @@ "xpack.snapshotRestore.policyDetails.includeGlobalStateFalseLabel": "いいえ", "xpack.snapshotRestore.policyDetails.includeGlobalStateLabel": "グローバルステータスを含める", "xpack.snapshotRestore.policyDetails.includeGlobalStateTrueLabel": "はい", - "xpack.snapshotRestore.policyDetails.indicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示", "xpack.snapshotRestore.policyDetails.indicesLabel": "インデックス", - "xpack.snapshotRestore.policyDetails.indicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示", "xpack.snapshotRestore.policyDetails.inProgressSnapshotLinkText": "「{snapshotName}」が進行中", "xpack.snapshotRestore.policyDetails.lastFailure.dateLabel": "日付", "xpack.snapshotRestore.policyDetails.lastFailure.detailsAriaLabel": "ポリシー「{name}」の前回のエラーの詳細", @@ -11666,7 +11663,6 @@ "xpack.snapshotRestore.policyForm.stepReview.retentionTab.maxCountLabel": "最高カウント", "xpack.snapshotRestore.policyForm.stepReview.retentionTab.minCountLabel": "最低カウント", "xpack.snapshotRestore.policyForm.stepReview.retentionTab.sectionRetentionTitle": "スナップショットの保存", - "xpack.snapshotRestore.policyForm.stepReview.summaryTab.allIndicesValue": "すべてのインデックス", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.editStepTooltip": "編集", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableFalseLabel": "いいえ", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableLabel": "利用不可能なインデックスを無視", @@ -11674,9 +11670,7 @@ "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateFalseLabel": "いいえ", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateLabel": "グローバルステータスを含める", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateTrueLabel": "はい", - "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesLabel": "インデックス", - "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.nameLabel": "ポリシー名", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialFalseLabel": "いいえ", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialLabel": "部分シャードを許可", @@ -12068,16 +12062,13 @@ "xpack.snapshotRestore.restoreForm.stepLogisticsTitle": "詳細を復元", "xpack.snapshotRestore.restoreForm.stepReview.jsonTab.jsonAriaLabel": "実行する設定を復元", "xpack.snapshotRestore.restoreForm.stepReview.jsonTabTitle": "JSON", - "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.allIndicesValue": "すべてのインデックス", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.editStepTooltip": "編集", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.ignoreIndexSettingsLabel": "リセット", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateFalseValue": "いいえ", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateLabel": "グローバル状態の復元", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateTrueValue": "はい", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indexSettingsLabel": "修正", - "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesLabel": "インデックス", - "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.noSettingsValue": "インデックス設定の修正はありません", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialFalseValue": "いいえ", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialLabel": "部分復元", @@ -12168,10 +12159,7 @@ "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateLabel": "グローバルステータスを含める", "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateNoLabel": "いいえ", "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateYesLabel": "はい", - "xpack.snapshotRestore.snapshotDetails.itemIndicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示", "xpack.snapshotRestore.snapshotDetails.itemIndicesLabel": "インデックス ({indicesCount})", - "xpack.snapshotRestore.snapshotDetails.itemIndicesNoneLabel": "-", - "xpack.snapshotRestore.snapshotDetails.itemIndicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示", "xpack.snapshotRestore.snapshotDetails.itemStartTimeLabel": "開始時刻", "xpack.snapshotRestore.snapshotDetails.itemStateLabel": "ステータス", "xpack.snapshotRestore.snapshotDetails.itemUuidLabel": "UUID", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a68658b86e5e8e..8409d10811e5a4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11549,7 +11549,6 @@ "xpack.snapshotRestore.home.snapshotRestoreTitle": "快照存储库", "xpack.snapshotRestore.home.snapshotsTabTitle": "快照", "xpack.snapshotRestore.policies.breadcrumbTitle": "策略", - "xpack.snapshotRestore.policyDetails.allIndicesLabel": "所有索引", "xpack.snapshotRestore.policyDetails.closeButtonLabel": "关闭", "xpack.snapshotRestore.policyDetails.deleteButtonLabel": "删除", "xpack.snapshotRestore.policyDetails.editButtonLabel": "编辑", @@ -11563,9 +11562,7 @@ "xpack.snapshotRestore.policyDetails.includeGlobalStateFalseLabel": "否", "xpack.snapshotRestore.policyDetails.includeGlobalStateLabel": "包括全局状态", "xpack.snapshotRestore.policyDetails.includeGlobalStateTrueLabel": "是", - "xpack.snapshotRestore.policyDetails.indicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}", "xpack.snapshotRestore.policyDetails.indicesLabel": "索引", - "xpack.snapshotRestore.policyDetails.indicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}", "xpack.snapshotRestore.policyDetails.inProgressSnapshotLinkText": "“{snapshotName}”正在进行中", "xpack.snapshotRestore.policyDetails.lastFailure.dateLabel": "日期", "xpack.snapshotRestore.policyDetails.lastFailure.detailsAriaLabel": "策略“{name}”的上次失败详情", @@ -11665,7 +11662,6 @@ "xpack.snapshotRestore.policyForm.stepReview.retentionTab.maxCountLabel": "最大计数", "xpack.snapshotRestore.policyForm.stepReview.retentionTab.minCountLabel": "最小计数", "xpack.snapshotRestore.policyForm.stepReview.retentionTab.sectionRetentionTitle": "快照保留", - "xpack.snapshotRestore.policyForm.stepReview.summaryTab.allIndicesValue": "所有索引", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.editStepTooltip": "编辑", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableFalseLabel": "否", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableLabel": "忽略不可用索引", @@ -11673,9 +11669,7 @@ "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateFalseLabel": "否", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateLabel": "包括全局状态", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateTrueLabel": "是", - "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesLabel": "索引", - "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.nameLabel": "策略名称", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialFalseLabel": "否", "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialLabel": "允许部分分片", @@ -12067,16 +12061,13 @@ "xpack.snapshotRestore.restoreForm.stepLogisticsTitle": "还原详情", "xpack.snapshotRestore.restoreForm.stepReview.jsonTab.jsonAriaLabel": "还原要执行的设置", "xpack.snapshotRestore.restoreForm.stepReview.jsonTabTitle": "JSON", - "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.allIndicesValue": "所有索引", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.editStepTooltip": "缂栬緫", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.ignoreIndexSettingsLabel": "重置", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateFalseValue": "否", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateLabel": "还原全局状态", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateTrueValue": "鏄", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indexSettingsLabel": "修改", - "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesLabel": "索引", - "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.noSettingsValue": "无索引设置修改", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialFalseValue": "否", "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialLabel": "部分还原", @@ -12167,10 +12158,7 @@ "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateLabel": "包括全局状态", "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateNoLabel": "否", "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateYesLabel": "是", - "xpack.snapshotRestore.snapshotDetails.itemIndicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}", "xpack.snapshotRestore.snapshotDetails.itemIndicesLabel": "索引 ({indicesCount})", - "xpack.snapshotRestore.snapshotDetails.itemIndicesNoneLabel": "-", - "xpack.snapshotRestore.snapshotDetails.itemIndicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}", "xpack.snapshotRestore.snapshotDetails.itemStartTimeLabel": "开始时间", "xpack.snapshotRestore.snapshotDetails.itemStateLabel": "状态", "xpack.snapshotRestore.snapshotDetails.itemUuidLabel": "UUID", From ce9708b78ebe00c06852bfefddf45dcc0d86ea87 Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Fri, 21 Feb 2020 01:43:35 -0500 Subject: [PATCH 15/19] Fix Snapshots Policies Alignment Issue in IE11 (#54866) (#58064) * Removed flex group because it's causing alignment issues on IE. Verified and tested on all 3 browsers. * Restored but added grow=false to properly align icons. --- .../sections/home/policy_list/policy_table/policy_table.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx index 0a5e9558f91af6..2493a8fbd9ffb1 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx @@ -199,7 +199,7 @@ export const PolicyTable: React.FunctionComponent = ({ render: ({ name, inProgress, isManagedPolicy }: SlmPolicy) => { return ( - + {executePolicyPrompt => { return ( @@ -235,7 +235,7 @@ export const PolicyTable: React.FunctionComponent = ({ }} - + = ({ /> - + {deletePolicyPrompt => { const label = !isManagedPolicy From e0ccf0d3e477c77b608c27dc4b42362466d4f389 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 21 Feb 2020 07:46:45 +0100 Subject: [PATCH 16/19] Drilldown plugin (#58097) (#58184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add component * feat: 🎸 use presentational component * feat: 🎸 create stubs for Drilldown components * feat: 🎸 open new drilldown flyout from panel's context menu * feat: 🎸 setup Drilldowns plugin in X-Pack * feat: 🎸 add Storybook to drilldowns plugin * refactor: 💡 move drilldown components to x-pack * feat: 🎸 add stub action to open drilldown flyout * feat: 🎸 add drilldowns plugin to translation index * fix: 🐛 correct TypeScript type check * fix: 🐛 use correct i18n namespace * ci: 🎡 add drilldowns plugin to CODEOWNERS file * fix: 🐛 revert back change * fix: 🐛 type must not be empty --- src/dev/storybook/aliases.ts | 1 + .../components/embeddable_panel/index.tsx | 29 ------ .../panel_options_menu.examples.tsx} | 33 ++++++- .../components/panel_options_menu/index.tsx | 93 +++++++++++++++++++ .../public/lib/panel/_embeddable_panel.scss | 1 - x-pack/.i18nrc.json | 18 ++-- x-pack/plugins/drilldowns/README.md | 3 + x-pack/plugins/drilldowns/kibana.json | 10 ++ .../drilldowns/public/actions/index.ts | 7 ++ .../open_flyout_add_drilldown/index.tsx | 50 ++++++++++ .../drilldown_hello_bar.examples.tsx | 13 +++ .../components/drilldown_hello_bar/index.tsx | 27 ++++++ .../drilldown_picker.examples.tsx | 13 +++ .../components/drilldown_picker/index.tsx | 21 +++++ .../form_create_drilldown.examples.tsx | 13 +++ .../components/form_create_drilldown/i18n.ts | 28 ++++++ .../form_create_drilldown/index.tsx | 30 ++++++ x-pack/plugins/drilldowns/public/index.ts | 18 ++++ x-pack/plugins/drilldowns/public/mocks.ts | 28 ++++++ x-pack/plugins/drilldowns/public/plugin.ts | 45 +++++++++ .../public/service/drilldown_service.ts | 28 ++++++ .../drilldowns/public/service/index.ts | 7 ++ .../plugins/drilldowns/scripts/storybook.js | 13 +++ 23 files changed, 489 insertions(+), 40 deletions(-) delete mode 100644 src/plugins/embeddable/public/components/embeddable_panel/index.tsx rename src/plugins/embeddable/public/components/{embeddable_panel/__examples__/embeddable_panel.examples.tsx => panel_options_menu/__examples__/panel_options_menu.examples.tsx} (53%) create mode 100644 src/plugins/embeddable/public/components/panel_options_menu/index.tsx create mode 100644 x-pack/plugins/drilldowns/README.md create mode 100644 x-pack/plugins/drilldowns/kibana.json create mode 100644 x-pack/plugins/drilldowns/public/actions/index.ts create mode 100644 x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts create mode 100644 x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx create mode 100644 x-pack/plugins/drilldowns/public/index.ts create mode 100644 x-pack/plugins/drilldowns/public/mocks.ts create mode 100644 x-pack/plugins/drilldowns/public/plugin.ts create mode 100644 x-pack/plugins/drilldowns/public/service/drilldown_service.ts create mode 100644 x-pack/plugins/drilldowns/public/service/index.ts create mode 100644 x-pack/plugins/drilldowns/scripts/storybook.js diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 1dce53b6c2a843..fb91b865097faf 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -20,6 +20,7 @@ export const storybookAliases = { apm: 'x-pack/legacy/plugins/apm/scripts/storybook.js', canvas: 'x-pack/legacy/plugins/canvas/scripts/storybook_new.js', + drilldowns: 'x-pack/plugins/drilldowns/scripts/storybook.js', embeddable: 'src/plugins/embeddable/scripts/storybook.js', infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js', siem: 'x-pack/legacy/plugins/siem/scripts/storybook.js', diff --git a/src/plugins/embeddable/public/components/embeddable_panel/index.tsx b/src/plugins/embeddable/public/components/embeddable_panel/index.tsx deleted file mode 100644 index 7089efa4bca88f..00000000000000 --- a/src/plugins/embeddable/public/components/embeddable_panel/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 { EuiPanel } from '@elastic/eui'; -import * as React from 'react'; - -export const EmbeddablePanel = () => { - return ( - - Hello world - - ); -}; diff --git a/src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx b/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx similarity index 53% rename from src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx rename to src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx index 7ec8848b8cebdd..33724068a6ba83 100644 --- a/src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx +++ b/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx @@ -19,6 +19,35 @@ import * as React from 'react'; import { storiesOf } from '@storybook/react'; -import { EmbeddablePanel } from '..'; +import { action } from '@storybook/addon-actions'; +import { withKnobs, boolean } from '@storybook/addon-knobs'; +import { PanelOptionsMenu } from '..'; -storiesOf('components/EmbeddablePanel', module).add('default', () => ); +const euiContextDescriptors = { + id: 'mainMenu', + title: 'Options', + items: [ + { + name: 'Inspect', + icon: 'inspect', + onClick: action('onClick(inspect)'), + }, + { + name: 'Full screen', + icon: 'expand', + onClick: action('onClick(expand)'), + }, + ], +}; + +storiesOf('components/PanelOptionsMenu', module) + .addDecorator(withKnobs) + .add('default', () => { + const isViewMode = boolean('isViewMode', false); + + return ( +
+ +
+ ); + }); diff --git a/src/plugins/embeddable/public/components/panel_options_menu/index.tsx b/src/plugins/embeddable/public/components/panel_options_menu/index.tsx new file mode 100644 index 00000000000000..4a950272695873 --- /dev/null +++ b/src/plugins/embeddable/public/components/panel_options_menu/index.tsx @@ -0,0 +1,93 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { useState, useEffect } from 'react'; +import { + EuiButtonIcon, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiPopover, +} from '@elastic/eui'; + +export interface PanelOptionsMenuProps { + panelDescriptor?: EuiContextMenuPanelDescriptor; + close?: boolean; + isViewMode?: boolean; + title?: string; +} + +export const PanelOptionsMenu: React.FC = ({ + panelDescriptor, + close, + isViewMode, + title, +}) => { + const [open, setOpen] = useState(false); + useEffect(() => { + if (!close) setOpen(false); + }, [close]); + + const handleContextMenuClick = () => { + setOpen(isOpen => !isOpen); + }; + + const handlePopoverClose = () => { + setOpen(false); + }; + + const enhancedAriaLabel = i18n.translate( + 'embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel', + { + defaultMessage: 'Panel options for {title}', + values: { title }, + } + ); + const ariaLabelWithoutTitle = i18n.translate( + 'embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabel', + { + defaultMessage: 'Panel options', + } + ); + + const button = ( + + ); + + return ( + + + + ); +}; diff --git a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss index 52a9ea594ff1df..9de20b73af0f8a 100644 --- a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss +++ b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss @@ -100,7 +100,6 @@ } } -.embPanel__optionsMenuPopover[class*='-isOpen'], .embPanel:hover { .embPanel__optionsMenuButton { opacity: 1; diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 9931d56a99a42f..f22f7e98d3b8a4 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -4,12 +4,14 @@ "xpack.actions": "plugins/actions", "xpack.advancedUiActions": "plugins/advanced_ui_actions", "xpack.alerting": "plugins/alerting", - "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.apm": ["legacy/plugins/apm", "plugins/apm"], "xpack.beatsManagement": "legacy/plugins/beats_management", "xpack.canvas": "legacy/plugins/canvas", "xpack.crossClusterReplication": "legacy/plugins/cross_cluster_replication", "xpack.dashboardMode": "legacy/plugins/dashboard_mode", + "xpack.data": "plugins/data_enhanced", + "xpack.drilldowns": "plugins/drilldowns", + "xpack.endpoint": "plugins/endpoint", "xpack.features": "plugins/features", "xpack.fileUpload": "legacy/plugins/file_upload", "xpack.graph": ["legacy/plugins/graph", "plugins/graph"], @@ -17,30 +19,30 @@ "xpack.idxMgmt": "legacy/plugins/index_management", "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management", "xpack.infra": "plugins/infra", - "xpack.data": "plugins/data_enhanced", + "xpack.ingestManager": "plugins/ingest_manager", "xpack.lens": "legacy/plugins/lens", - "xpack.licensing": "plugins/licensing", "xpack.licenseMgmt": "legacy/plugins/license_management", - "xpack.maps": "legacy/plugins/maps", - "xpack.ml": "legacy/plugins/ml", + "xpack.licensing": "plugins/licensing", "xpack.logstash": "legacy/plugins/logstash", "xpack.main": "legacy/plugins/xpack_main", + "xpack.maps": "legacy/plugins/maps", + "xpack.ml": "legacy/plugins/ml", "xpack.monitoring": "legacy/plugins/monitoring", "xpack.remoteClusters": "plugins/remote_clusters", "xpack.reporting": ["plugins/reporting", "legacy/plugins/reporting"], "xpack.rollupJobs": "legacy/plugins/rollup", "xpack.searchProfiler": "plugins/searchprofiler", - "xpack.siem": "legacy/plugins/siem", "xpack.security": ["legacy/plugins/security", "plugins/security"], "xpack.server": "legacy/server", + "xpack.siem": "legacy/plugins/siem", "xpack.snapshotRestore": "legacy/plugins/snapshot_restore", "xpack.spaces": ["legacy/plugins/spaces", "plugins/spaces"], "xpack.taskManager": "legacy/plugins/task_manager", "xpack.transform": "legacy/plugins/transform", + "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant", "xpack.uptime": "legacy/plugins/uptime", - "xpack.watcher": "plugins/watcher", - "xpack.endpoint": "plugins/endpoint" + "xpack.watcher": "plugins/watcher" }, "translations": [ "plugins/translations/translations/zh-CN.json", diff --git a/x-pack/plugins/drilldowns/README.md b/x-pack/plugins/drilldowns/README.md new file mode 100644 index 00000000000000..701b6082d4985f --- /dev/null +++ b/x-pack/plugins/drilldowns/README.md @@ -0,0 +1,3 @@ +# Drilldowns + +Provides functionality to navigate between Kibana apps with context information. diff --git a/x-pack/plugins/drilldowns/kibana.json b/x-pack/plugins/drilldowns/kibana.json new file mode 100644 index 00000000000000..b951c7dc1fc875 --- /dev/null +++ b/x-pack/plugins/drilldowns/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "drilldowns", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": [ + "uiActions", + "embeddable" + ] +} diff --git a/x-pack/plugins/drilldowns/public/actions/index.ts b/x-pack/plugins/drilldowns/public/actions/index.ts new file mode 100644 index 00000000000000..c0ca7fac22049b --- /dev/null +++ b/x-pack/plugins/drilldowns/public/actions/index.ts @@ -0,0 +1,7 @@ +/* + * 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 * from './open_flyout_add_drilldown'; diff --git a/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx b/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx new file mode 100644 index 00000000000000..06f134b10a4b7d --- /dev/null +++ b/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx @@ -0,0 +1,50 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { CoreStart } from 'src/core/public'; +import { Action } from '../../../../../../src/plugins/ui_actions/public'; +import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; +import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public'; +import { FormCreateDrilldown } from '../../components/form_create_drilldown'; + +export const OPEN_FLYOUT_ADD_DRILLDOWN = 'OPEN_FLYOUT_ADD_DRILLDOWN'; + +interface ActionContext { + embeddable: IEmbeddable; +} + +export interface OpenFlyoutAddDrilldownParams { + overlays: () => Promise; +} + +export class OpenFlyoutAddDrilldown implements Action { + public readonly type = OPEN_FLYOUT_ADD_DRILLDOWN; + public readonly id = OPEN_FLYOUT_ADD_DRILLDOWN; + public order = 100; + + constructor(protected readonly params: OpenFlyoutAddDrilldownParams) {} + + public getDisplayName() { + return i18n.translate('xpack.drilldowns.panel.openFlyoutAddDrilldown.displayName', { + defaultMessage: 'Add drilldown', + }); + } + + public getIconType() { + return 'empty'; + } + + public async isCompatible({ embeddable }: ActionContext) { + return true; + } + + public async execute({ embeddable }: ActionContext) { + const overlays = await this.params.overlays(); + overlays.openFlyout(toMountPoint()); + } +} diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx new file mode 100644 index 00000000000000..afa82f5e74c16b --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx @@ -0,0 +1,13 @@ +/* + * 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 React from 'react'; +import { storiesOf } from '@storybook/react'; +import { DrilldownHelloBar } from '..'; + +storiesOf('components/DrilldownHelloBar', module).add('default', () => { + return ; +}); diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx new file mode 100644 index 00000000000000..895a100df3ac50 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx @@ -0,0 +1,27 @@ +/* + * 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'; + +export interface DrilldownHelloBarProps { + docsLink?: string; +} + +export const DrilldownHelloBar: React.FC = ({ docsLink }) => { + return ( +
+

+ Drilldowns provide the ability to define a new behavior when interacting with a panel. You + can add multiple options or simply override the default filtering behavior. +

+ View docs + +
+ ); +}; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx new file mode 100644 index 00000000000000..dfdd9627ab5cd4 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx @@ -0,0 +1,13 @@ +/* + * 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 React from 'react'; +import { storiesOf } from '@storybook/react'; +import { DrilldownPicker } from '..'; + +storiesOf('components/DrilldownPicker', module).add('default', () => { + return ; +}); diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx new file mode 100644 index 00000000000000..3748fc666c81c5 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx @@ -0,0 +1,21 @@ +/* + * 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'; + +// eslint-disable-next-line +export interface DrilldownPickerProps {} + +export const DrilldownPicker: React.FC = () => { + return ( + + ); +}; diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx new file mode 100644 index 00000000000000..34f6932b41dacf --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx @@ -0,0 +1,13 @@ +/* + * 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 React from 'react'; +import { storiesOf } from '@storybook/react'; +import { FormCreateDrilldown } from '..'; + +storiesOf('components/FormCreateDrilldown', module).add('default', () => { + return ; +}); diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts new file mode 100644 index 00000000000000..922131ba4b9012 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts @@ -0,0 +1,28 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const txtNameOfDrilldown = i18n.translate( + 'xpack.drilldowns.components.form_create_drilldown.nameOfDrilldown', + { + defaultMessage: 'Name of drilldown', + } +); + +export const txtUntitledDrilldown = i18n.translate( + 'xpack.drilldowns.components.form_create_drilldown.untitledDrilldown', + { + defaultMessage: 'Untitled drilldown', + } +); + +export const txtDrilldownAction = i18n.translate( + 'xpack.drilldowns.components.form_create_drilldown.drilldownAction', + { + defaultMessage: 'Drilldown action', + } +); diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx new file mode 100644 index 00000000000000..40cd4cf2b210b8 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiForm, EuiFormRow, EuiFieldText } from '@elastic/eui'; +import { DrilldownHelloBar } from '../drilldown_hello_bar'; +import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n'; +import { DrilldownPicker } from '../drilldown_picker'; + +// eslint-disable-next-line +export interface FormCreateDrilldownProps {} + +export const FormCreateDrilldown: React.FC = () => { + return ( +
+ + + + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/drilldowns/public/index.ts b/x-pack/plugins/drilldowns/public/index.ts new file mode 100644 index 00000000000000..63e7a122354620 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/index.ts @@ -0,0 +1,18 @@ +/* + * 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 { DrilldownsPlugin } from './plugin'; + +export { + DrilldownsSetupContract, + DrilldownsSetupDependencies, + DrilldownsStartContract, + DrilldownsStartDependencies, +} from './plugin'; + +export function plugin() { + return new DrilldownsPlugin(); +} diff --git a/x-pack/plugins/drilldowns/public/mocks.ts b/x-pack/plugins/drilldowns/public/mocks.ts new file mode 100644 index 00000000000000..bfade1674072ac --- /dev/null +++ b/x-pack/plugins/drilldowns/public/mocks.ts @@ -0,0 +1,28 @@ +/* + * 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 { DrilldownsSetupContract, DrilldownsStartContract } from '.'; + +export type Setup = jest.Mocked; +export type Start = jest.Mocked; + +const createSetupContract = (): Setup => { + const setupContract: Setup = { + registerDrilldown: jest.fn(), + }; + return setupContract; +}; + +const createStartContract = (): Start => { + const startContract: Start = {}; + + return startContract; +}; + +export const bfetchPluginMock = { + createSetupContract, + createStartContract, +}; diff --git a/x-pack/plugins/drilldowns/public/plugin.ts b/x-pack/plugins/drilldowns/public/plugin.ts new file mode 100644 index 00000000000000..6c8555fa55a119 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/plugin.ts @@ -0,0 +1,45 @@ +/* + * 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 { CoreStart, CoreSetup, Plugin } from 'src/core/public'; +import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public'; +import { DrilldownService } from './service'; + +export interface DrilldownsSetupDependencies { + uiActions: UiActionsSetup; +} + +export interface DrilldownsStartDependencies { + uiActions: UiActionsStart; +} + +export type DrilldownsSetupContract = Pick; + +// eslint-disable-next-line +export interface DrilldownsStartContract {} + +export class DrilldownsPlugin + implements + Plugin< + DrilldownsSetupContract, + DrilldownsStartContract, + DrilldownsSetupDependencies, + DrilldownsStartDependencies + > { + private readonly service = new DrilldownService(); + + public setup(core: CoreSetup, plugins: DrilldownsSetupDependencies): DrilldownsSetupContract { + this.service.bootstrap(core, plugins); + + return this.service; + } + + public start(core: CoreStart, plugins: DrilldownsStartDependencies): DrilldownsStartContract { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/drilldowns/public/service/drilldown_service.ts b/x-pack/plugins/drilldowns/public/service/drilldown_service.ts new file mode 100644 index 00000000000000..f22f4521816480 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/service/drilldown_service.ts @@ -0,0 +1,28 @@ +/* + * 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 { CoreSetup } from 'src/core/public'; +import { OpenFlyoutAddDrilldown } from '../actions/open_flyout_add_drilldown'; +import { DrilldownsSetupDependencies } from '../plugin'; + +export class DrilldownService { + bootstrap(core: CoreSetup, { uiActions }: DrilldownsSetupDependencies) { + const actionOpenFlyoutAddDrilldown = new OpenFlyoutAddDrilldown({ + overlays: async () => (await core.getStartServices())[0].overlays, + }); + + uiActions.registerAction(actionOpenFlyoutAddDrilldown); + uiActions.attachAction('CONTEXT_MENU_TRIGGER', actionOpenFlyoutAddDrilldown.id); + } + + /** + * Convenience method to register a drilldown. (It should set-up all the + * necessary triggers and actions.) + */ + registerDrilldown = (): void => { + throw new Error('not implemented'); + }; +} diff --git a/x-pack/plugins/drilldowns/public/service/index.ts b/x-pack/plugins/drilldowns/public/service/index.ts new file mode 100644 index 00000000000000..44472b18a53172 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/service/index.ts @@ -0,0 +1,7 @@ +/* + * 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 * from './drilldown_service'; diff --git a/x-pack/plugins/drilldowns/scripts/storybook.js b/x-pack/plugins/drilldowns/scripts/storybook.js new file mode 100644 index 00000000000000..9b0f57746e5849 --- /dev/null +++ b/x-pack/plugins/drilldowns/scripts/storybook.js @@ -0,0 +1,13 @@ +/* + * 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 { join } from 'path'; + +// eslint-disable-next-line +require('@kbn/storybook').runStorybookCli({ + name: 'drilldowns', + storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.examples.tsx')], +}); From ac0166064dd0f898fb667903257a9f0b3faa6892 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Fri, 21 Feb 2020 11:27:36 +0100 Subject: [PATCH 17/19] [ML] New Platform server shim: update indices routes (#57685) (#57778) * [ML] NP indices routes * [ML] fix error function * [ML] fix createAndOpenUrl function Co-authored-by: Elastic Machine --- .../components/anomalies_table/links_menu.js | 70 +++++++++---------- .../plugins/ml/server/routes/indices.js | 28 -------- .../plugins/ml/server/routes/indices.ts | 49 +++++++++++++ 3 files changed, 84 insertions(+), 63 deletions(-) delete mode 100644 x-pack/legacy/plugins/ml/server/routes/indices.js create mode 100644 x-pack/legacy/plugins/ml/server/routes/indices.ts diff --git a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js index c16dc37097b133..f161e37efa8d8c 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js +++ b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js @@ -236,26 +236,26 @@ class LinksMenuUI extends Component { let i = 0; findFieldType(datafeedIndices[i]); - function findFieldType(index) { - getFieldTypeFromMapping(index, categorizationFieldName) - .then(resp => { - if (resp !== '') { - createAndOpenUrl(index, resp); - } else { - i++; - if (i < datafeedIndices.length) { - findFieldType(datafeedIndices[i]); - } else { - error(); - } - } + const error = () => { + console.log( + `viewExamples(): error finding type of field ${categorizationFieldName} in indices:`, + datafeedIndices + ); + const { toasts } = this.props.kibana.services.notifications; + toasts.addDanger( + i18n.translate('xpack.ml.anomaliesTable.linksMenu.noMappingCouldBeFoundErrorMessage', { + defaultMessage: + 'Unable to view examples of documents with mlcategory {categoryId} ' + + 'as no mapping could be found for the categorization field {categorizationFieldName}', + values: { + categoryId, + categorizationFieldName, + }, }) - .catch(() => { - error(); - }); - } + ); + }; - function createAndOpenUrl(index, categorizationFieldType) { + const createAndOpenUrl = (index, categorizationFieldType) => { // Find the ID of the index pattern with a title attribute which matches the // index configured in the datafeed. If a Kibana index pattern has not been created // for this index, then the user will see a warning message on the Discover tab advising @@ -340,25 +340,25 @@ class LinksMenuUI extends Component { }) ); }); - } + }; - function error() { - console.log( - `viewExamples(): error finding type of field ${categorizationFieldName} in indices:`, - datafeedIndices - ); - const { toasts } = this.props.kibana.services.notifications; - toasts.addDanger( - i18n.translate('xpack.ml.anomaliesTable.linksMenu.noMappingCouldBeFoundErrorMessage', { - defaultMessage: - 'Unable to view examples of documents with mlcategory {categoryId} ' + - 'as no mapping could be found for the categorization field {categorizationFieldName}', - values: { - categoryId, - categorizationFieldName, - }, + function findFieldType(index) { + getFieldTypeFromMapping(index, categorizationFieldName) + .then(resp => { + if (resp !== '') { + createAndOpenUrl(index, resp); + } else { + i++; + if (i < datafeedIndices.length) { + findFieldType(datafeedIndices[i]); + } else { + error(); + } + } }) - ); + .catch(() => { + error(); + }); } }; diff --git a/x-pack/legacy/plugins/ml/server/routes/indices.js b/x-pack/legacy/plugins/ml/server/routes/indices.js deleted file mode 100644 index 309b41e53eef50..00000000000000 --- a/x-pack/legacy/plugins/ml/server/routes/indices.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { callWithRequestFactory } from '../client/call_with_request_factory'; -import { wrapError } from '../client/errors'; - -export function indicesRoutes({ commonRouteConfig, elasticsearchPlugin, route }) { - route({ - method: 'POST', - path: '/api/ml/indices/field_caps', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const index = request.payload.index; - let fields = '*'; - if (request.payload.fields !== undefined && Array.isArray(request.payload.fields)) { - fields = request.payload.fields.join(','); - } - - return callWithRequest('fieldCaps', { index, fields }).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); -} diff --git a/x-pack/legacy/plugins/ml/server/routes/indices.ts b/x-pack/legacy/plugins/ml/server/routes/indices.ts new file mode 100644 index 00000000000000..0ee15f1321e9c2 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/routes/indices.ts @@ -0,0 +1,49 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { wrapError } from '../client/error_wrapper'; +import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory'; +import { RouteInitialization } from '../new_platform/plugin'; + +/** + * Indices routes. + */ +export function indicesRoutes({ xpackMainPlugin, router }: RouteInitialization) { + /** + * @apiGroup Indices + * + * @api {post} /api/ml/indices/field_caps + * @apiName FieldCaps + * @apiDescription Retrieves the capabilities of fields among multiple indices. + */ + router.post( + { + path: '/api/ml/indices/field_caps', + validate: { + body: schema.object({ + index: schema.maybe(schema.string()), + fields: schema.maybe(schema.arrayOf(schema.string())), + }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const { + body: { index, fields: requestFields }, + } = request; + const fields = + requestFields !== undefined && Array.isArray(requestFields) + ? requestFields.join(',') + : '*'; + const result = await context.ml!.mlClient.callAsCurrentUser('fieldCaps', { index, fields }); + return response.ok({ body: result }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); +} From 9b6b1402c2290365520472b959b3dd3ad404a843 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 21 Feb 2020 11:30:20 +0100 Subject: [PATCH 18/19] Expose elasticsearch config schema (#57655) (#58199) * expose elasticsearch config schema * update docs * mark export as alpha since it can be deleted Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- ...erver.elasticsearchconfig._constructor_.md | 20 ++++++ ...n-server.elasticsearchconfig.apiversion.md | 13 ++++ ...erver.elasticsearchconfig.customheaders.md | 13 ++++ ...er.elasticsearchconfig.healthcheckdelay.md | 13 ++++ ...plugin-server.elasticsearchconfig.hosts.md | 13 ++++ ...asticsearchconfig.ignoreversionmismatch.md | 13 ++++ ...n-server.elasticsearchconfig.logqueries.md | 13 ++++ ...ibana-plugin-server.elasticsearchconfig.md | 41 +++++++++++ ...gin-server.elasticsearchconfig.password.md | 13 ++++ ...-server.elasticsearchconfig.pingtimeout.md | 13 ++++ ...ticsearchconfig.requestheaderswhitelist.md | 13 ++++ ...rver.elasticsearchconfig.requesttimeout.md | 13 ++++ ...server.elasticsearchconfig.shardtimeout.md | 13 ++++ ...erver.elasticsearchconfig.sniffinterval.md | 13 ++++ ...sticsearchconfig.sniffonconnectionfault.md | 13 ++++ ...server.elasticsearchconfig.sniffonstart.md | 13 ++++ ...a-plugin-server.elasticsearchconfig.ssl.md | 15 ++++ ...gin-server.elasticsearchconfig.username.md | 13 ++++ .../core/server/kibana-plugin-server.md | 1 + .../elasticsearch/elasticsearch_config.ts | 11 ++- src/core/server/elasticsearch/index.ts | 2 +- src/core/server/index.ts | 19 ++++- src/core/server/server.api.md | 71 ++++++++++++++++++- 23 files changed, 369 insertions(+), 6 deletions(-) create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md new file mode 100644 index 00000000000000..55e5cf74fc512c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [(constructor)](./kibana-plugin-server.elasticsearchconfig._constructor_.md) + +## ElasticsearchConfig.(constructor) + +Constructs a new instance of the `ElasticsearchConfig` class + +Signature: + +```typescript +constructor(rawConfig: ElasticsearchConfigType); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| rawConfig | ElasticsearchConfigType | | + diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md new file mode 100644 index 00000000000000..097654f1fb090a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [apiVersion](./kibana-plugin-server.elasticsearchconfig.apiversion.md) + +## ElasticsearchConfig.apiVersion property + +Version of the Elasticsearch (6.7, 7.1 or `master`) client will be connecting to. + +Signature: + +```typescript +readonly apiVersion: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md new file mode 100644 index 00000000000000..0b3998e59c5df2 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [customHeaders](./kibana-plugin-server.elasticsearchconfig.customheaders.md) + +## ElasticsearchConfig.customHeaders property + +Header names and values to send to Elasticsearch with every request. These headers cannot be overwritten by client-side headers and aren't affected by `requestHeadersWhitelist` configuration. + +Signature: + +```typescript +readonly customHeaders: ElasticsearchConfigType['customHeaders']; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md new file mode 100644 index 00000000000000..b5589727d80aa4 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [healthCheckDelay](./kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md) + +## ElasticsearchConfig.healthCheckDelay property + +The interval between health check requests Kibana sends to the Elasticsearch. + +Signature: + +```typescript +readonly healthCheckDelay: Duration; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md new file mode 100644 index 00000000000000..29770ba5e0795f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [hosts](./kibana-plugin-server.elasticsearchconfig.hosts.md) + +## ElasticsearchConfig.hosts property + +Hosts that the client will connect to. If sniffing is enabled, this list will be used as seeds to discover the rest of your cluster. + +Signature: + +```typescript +readonly hosts: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md new file mode 100644 index 00000000000000..42e32f920c1dbf --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [ignoreVersionMismatch](./kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md) + +## ElasticsearchConfig.ignoreVersionMismatch property + +Whether to allow kibana to connect to a non-compatible elasticsearch node. + +Signature: + +```typescript +readonly ignoreVersionMismatch: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md new file mode 100644 index 00000000000000..64de7f65044508 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [logQueries](./kibana-plugin-server.elasticsearchconfig.logqueries.md) + +## ElasticsearchConfig.logQueries property + +Specifies whether all queries to the client should be logged (status code, method, query etc.). + +Signature: + +```typescript +readonly logQueries: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md new file mode 100644 index 00000000000000..e478dc7b966a25 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md @@ -0,0 +1,41 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) + +## ElasticsearchConfig class + +Wrapper of config schema. + +Signature: + +```typescript +export declare class ElasticsearchConfig +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(rawConfig)](./kibana-plugin-server.elasticsearchconfig._constructor_.md) | | Constructs a new instance of the ElasticsearchConfig class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [apiVersion](./kibana-plugin-server.elasticsearchconfig.apiversion.md) | | string | Version of the Elasticsearch (6.7, 7.1 or master) client will be connecting to. | +| [customHeaders](./kibana-plugin-server.elasticsearchconfig.customheaders.md) | | ElasticsearchConfigType['customHeaders'] | Header names and values to send to Elasticsearch with every request. These headers cannot be overwritten by client-side headers and aren't affected by requestHeadersWhitelist configuration. | +| [healthCheckDelay](./kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md) | | Duration | The interval between health check requests Kibana sends to the Elasticsearch. | +| [hosts](./kibana-plugin-server.elasticsearchconfig.hosts.md) | | string[] | Hosts that the client will connect to. If sniffing is enabled, this list will be used as seeds to discover the rest of your cluster. | +| [ignoreVersionMismatch](./kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md) | | boolean | Whether to allow kibana to connect to a non-compatible elasticsearch node. | +| [logQueries](./kibana-plugin-server.elasticsearchconfig.logqueries.md) | | boolean | Specifies whether all queries to the client should be logged (status code, method, query etc.). | +| [password](./kibana-plugin-server.elasticsearchconfig.password.md) | | string | If Elasticsearch is protected with basic authentication, this setting provides the password that the Kibana server uses to perform its administrative functions. | +| [pingTimeout](./kibana-plugin-server.elasticsearchconfig.pingtimeout.md) | | Duration | Timeout after which PING HTTP request will be aborted and retried. | +| [requestHeadersWhitelist](./kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md) | | string[] | List of Kibana client-side headers to send to Elasticsearch when request scoped cluster client is used. If this is an empty array then \*no\* client-side will be sent. | +| [requestTimeout](./kibana-plugin-server.elasticsearchconfig.requesttimeout.md) | | Duration | Timeout after which HTTP request will be aborted and retried. | +| [shardTimeout](./kibana-plugin-server.elasticsearchconfig.shardtimeout.md) | | Duration | Timeout for Elasticsearch to wait for responses from shards. Set to 0 to disable. | +| [sniffInterval](./kibana-plugin-server.elasticsearchconfig.sniffinterval.md) | | false | Duration | Interval to perform a sniff operation and make sure the list of nodes is complete. If false then sniffing is disabled. | +| [sniffOnConnectionFault](./kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md) | | boolean | Specifies whether the client should immediately sniff for a more current list of nodes when a connection dies. | +| [sniffOnStart](./kibana-plugin-server.elasticsearchconfig.sniffonstart.md) | | boolean | Specifies whether the client should attempt to detect the rest of the cluster when it is first instantiated. | +| [ssl](./kibana-plugin-server.elasticsearchconfig.ssl.md) | | Pick<SslConfigSchema, Exclude<keyof SslConfigSchema, 'certificateAuthorities' | 'keystore' | 'truststore'>> & {
certificateAuthorities?: string[];
} | Set of settings configure SSL connection between Kibana and Elasticsearch that are required when xpack.ssl.verification_mode in Elasticsearch is set to either certificate or full. | +| [username](./kibana-plugin-server.elasticsearchconfig.username.md) | | string | If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions. | + diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md new file mode 100644 index 00000000000000..ffe6f75e9874de --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [password](./kibana-plugin-server.elasticsearchconfig.password.md) + +## ElasticsearchConfig.password property + +If Elasticsearch is protected with basic authentication, this setting provides the password that the Kibana server uses to perform its administrative functions. + +Signature: + +```typescript +readonly password?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md new file mode 100644 index 00000000000000..09123f0969b601 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [pingTimeout](./kibana-plugin-server.elasticsearchconfig.pingtimeout.md) + +## ElasticsearchConfig.pingTimeout property + +Timeout after which PING HTTP request will be aborted and retried. + +Signature: + +```typescript +readonly pingTimeout: Duration; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md new file mode 100644 index 00000000000000..eeced56e3103f0 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [requestHeadersWhitelist](./kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md) + +## ElasticsearchConfig.requestHeadersWhitelist property + +List of Kibana client-side headers to send to Elasticsearch when request scoped cluster client is used. If this is an empty array then \*no\* client-side will be sent. + +Signature: + +```typescript +readonly requestHeadersWhitelist: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md new file mode 100644 index 00000000000000..dbd5ecb9396731 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [requestTimeout](./kibana-plugin-server.elasticsearchconfig.requesttimeout.md) + +## ElasticsearchConfig.requestTimeout property + +Timeout after which HTTP request will be aborted and retried. + +Signature: + +```typescript +readonly requestTimeout: Duration; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md new file mode 100644 index 00000000000000..aa923042bf64fc --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [shardTimeout](./kibana-plugin-server.elasticsearchconfig.shardtimeout.md) + +## ElasticsearchConfig.shardTimeout property + +Timeout for Elasticsearch to wait for responses from shards. Set to 0 to disable. + +Signature: + +```typescript +readonly shardTimeout: Duration; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md new file mode 100644 index 00000000000000..37fd2a7439535a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [sniffInterval](./kibana-plugin-server.elasticsearchconfig.sniffinterval.md) + +## ElasticsearchConfig.sniffInterval property + +Interval to perform a sniff operation and make sure the list of nodes is complete. If `false` then sniffing is disabled. + +Signature: + +```typescript +readonly sniffInterval: false | Duration; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md new file mode 100644 index 00000000000000..c703be548d34bc --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [sniffOnConnectionFault](./kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md) + +## ElasticsearchConfig.sniffOnConnectionFault property + +Specifies whether the client should immediately sniff for a more current list of nodes when a connection dies. + +Signature: + +```typescript +readonly sniffOnConnectionFault: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md new file mode 100644 index 00000000000000..26a7d9cc11a80d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [sniffOnStart](./kibana-plugin-server.elasticsearchconfig.sniffonstart.md) + +## ElasticsearchConfig.sniffOnStart property + +Specifies whether the client should attempt to detect the rest of the cluster when it is first instantiated. + +Signature: + +```typescript +readonly sniffOnStart: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md new file mode 100644 index 00000000000000..4d23c410f59fa2 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [ssl](./kibana-plugin-server.elasticsearchconfig.ssl.md) + +## ElasticsearchConfig.ssl property + +Set of settings configure SSL connection between Kibana and Elasticsearch that are required when `xpack.ssl.verification_mode` in Elasticsearch is set to either `certificate` or `full`. + +Signature: + +```typescript +readonly ssl: Pick> & { + certificateAuthorities?: string[]; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md new file mode 100644 index 00000000000000..d0098d656befbf --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) > [username](./kibana-plugin-server.elasticsearchconfig.username.md) + +## ElasticsearchConfig.username property + +If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions. + +Signature: + +```typescript +readonly username?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md index 482f014b226b9f..9ec443d6482e89 100644 --- a/docs/development/core/server/kibana-plugin-server.md +++ b/docs/development/core/server/kibana-plugin-server.md @@ -19,6 +19,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [BasePath](./kibana-plugin-server.basepath.md) | Access or manipulate the Kibana base path | | [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | | [CspConfig](./kibana-plugin-server.cspconfig.md) | CSP configuration for use in Kibana. | +| [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) | Wrapper of config schema. | | [ElasticsearchErrorHelpers](./kibana-plugin-server.elasticsearcherrorhelpers.md) | Helpers for working with errors returned from the Elasticsearch service.Since the internal data of errors are subject to change, consumers of the Elasticsearch service should always use these helpers to classify errors instead of checking error internals such as body.error.header[WWW-Authenticate] | | [KibanaRequest](./kibana-plugin-server.kibanarequest.md) | Kibana specific abstraction for an incoming request. | | [RouteValidationError](./kibana-plugin-server.routevalidationerror.md) | Error to return when the validation is not successful. | diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts index 2f4160ad75e9d8..df31f51361c191 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.ts @@ -31,7 +31,12 @@ export const DEFAULT_API_VERSION = '7.x'; export type ElasticsearchConfigType = TypeOf; type SslConfigSchema = ElasticsearchConfigType['ssl']; -const configSchema = schema.object({ +/** + * Validation schema for elasticsearch service config. It can be reused when plugins allow users + * to specify a local elasticsearch config. + * @public + */ +export const configSchema = schema.object({ sniffOnStart: schema.boolean({ defaultValue: false }), sniffInterval: schema.oneOf([schema.duration(), schema.literal(false)], { defaultValue: false, @@ -148,6 +153,10 @@ export const config: ServiceConfigDescriptor = { deprecations, }; +/** + * Wrapper of config schema. + * @public + */ export class ElasticsearchConfig { /** * The interval between health check requests Kibana sends to the Elasticsearch. diff --git a/src/core/server/elasticsearch/index.ts b/src/core/server/elasticsearch/index.ts index 5d64fadfaa1842..cfd72a6fd5e47b 100644 --- a/src/core/server/elasticsearch/index.ts +++ b/src/core/server/elasticsearch/index.ts @@ -27,7 +27,7 @@ export { } from './cluster_client'; export { IScopedClusterClient, ScopedClusterClient, Headers } from './scoped_cluster_client'; export { ElasticsearchClientConfig } from './elasticsearch_client_config'; -export { config } from './elasticsearch_config'; +export { config, configSchema, ElasticsearchConfig } from './elasticsearch_config'; export { ElasticsearchError, ElasticsearchErrorHelpers } from './errors'; export * from './api_types'; export * from './types'; diff --git a/src/core/server/index.ts b/src/core/server/index.ts index cc838ddd1351df..52827b72ee0cc0 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -39,7 +39,12 @@ * @packageDocumentation */ -import { ElasticsearchServiceSetup, IScopedClusterClient } from './elasticsearch'; +import { + ElasticsearchServiceSetup, + IScopedClusterClient, + configSchema as elasticsearchConfigSchema, +} from './elasticsearch'; + import { HttpServiceSetup } from './http'; import { IScopedRenderingClient } from './rendering'; import { PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId } from './plugins'; @@ -78,6 +83,7 @@ export { Headers, ScopedClusterClient, IScopedClusterClient, + ElasticsearchConfig, ElasticsearchClientConfig, ElasticsearchError, ElasticsearchErrorHelpers, @@ -347,3 +353,14 @@ export { PluginOpaqueId, UuidServiceSetup, }; + +/** + * Config schemas for the platform services. + * + * @alpha + */ +export const config = { + elasticsearch: { + schema: elasticsearchConfigSchema, + }, +}; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index ad1907df571fbe..053a60028fc5ff 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -503,6 +503,49 @@ export class ClusterClient implements IClusterClient { close(): void; } +// @alpha +export const config: { + elasticsearch: { + schema: import("@kbn/config-schema").ObjectType<{ + sniffOnStart: import("@kbn/config-schema").Type; + sniffInterval: import("@kbn/config-schema").Type; + sniffOnConnectionFault: import("@kbn/config-schema").Type; + hosts: import("@kbn/config-schema").Type; + preserveHost: import("@kbn/config-schema").Type; + username: import("@kbn/config-schema").Type; + password: import("@kbn/config-schema").Type; + requestHeadersWhitelist: import("@kbn/config-schema").Type; + customHeaders: import("@kbn/config-schema").Type>; + shardTimeout: import("@kbn/config-schema").Type; + requestTimeout: import("@kbn/config-schema").Type; + pingTimeout: import("@kbn/config-schema").Type; + startupTimeout: import("@kbn/config-schema").Type; + logQueries: import("@kbn/config-schema").Type; + ssl: import("@kbn/config-schema").ObjectType<{ + verificationMode: import("@kbn/config-schema").Type<"none" | "full" | "certificate">; + certificateAuthorities: import("@kbn/config-schema").Type; + certificate: import("@kbn/config-schema").Type; + key: import("@kbn/config-schema").Type; + keyPassphrase: import("@kbn/config-schema").Type; + keystore: import("@kbn/config-schema").ObjectType<{ + path: import("@kbn/config-schema").Type; + password: import("@kbn/config-schema").Type; + }>; + truststore: import("@kbn/config-schema").ObjectType<{ + path: import("@kbn/config-schema").Type; + password: import("@kbn/config-schema").Type; + }>; + alwaysPresentCertificate: import("@kbn/config-schema").Type; + }>; + apiVersion: import("@kbn/config-schema").Type; + healthCheck: import("@kbn/config-schema").ObjectType<{ + delay: import("@kbn/config-schema").Type; + }>; + ignoreVersionMismatch: import("@kbn/config-schema/target/types/types").ConditionalType; + }>; + }; +}; + // @public export type ConfigDeprecation = (config: Record, fromPath: string, logger: ConfigDeprecationLogger) => Record; @@ -650,8 +693,6 @@ export interface DiscoveredPlugin { readonly requiredPlugins: readonly PluginName[]; } -// Warning: (ae-forgotten-export) The symbol "ElasticsearchConfig" needs to be exported by the entry point index.d.ts -// // @public (undocumented) export type ElasticsearchClientConfig = Pick & Pick & { pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout']; @@ -660,6 +701,31 @@ export type ElasticsearchClientConfig = Pick; }; +// @public +export class ElasticsearchConfig { + constructor(rawConfig: ElasticsearchConfigType); + readonly apiVersion: string; + // Warning: (ae-forgotten-export) The symbol "ElasticsearchConfigType" needs to be exported by the entry point index.d.ts + readonly customHeaders: ElasticsearchConfigType['customHeaders']; + readonly healthCheckDelay: Duration; + readonly hosts: string[]; + readonly ignoreVersionMismatch: boolean; + readonly logQueries: boolean; + readonly password?: string; + readonly pingTimeout: Duration; + readonly requestHeadersWhitelist: string[]; + readonly requestTimeout: Duration; + readonly shardTimeout: Duration; + readonly sniffInterval: false | Duration; + readonly sniffOnConnectionFault: boolean; + readonly sniffOnStart: boolean; + // Warning: (ae-forgotten-export) The symbol "SslConfigSchema" needs to be exported by the entry point index.d.ts + readonly ssl: Pick> & { + certificateAuthorities?: string[]; + }; + readonly username?: string; +} + // @public (undocumented) export interface ElasticsearchError extends Boom { // (undocumented) @@ -2133,7 +2199,6 @@ export const validBodyOutput: readonly ["data", "stream"]; // src/core/server/plugins/plugins_service.ts:44:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:227:3 - (ae-forgotten-export) The symbol "ElasticsearchConfigType" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:228:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts ``` From 0d6ea902f3d46a2a5faa8829e800c7d82a6d916e Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 21 Feb 2020 11:30:46 +0100 Subject: [PATCH 19/19] document difference between log record formats (#57798) (#58200) * document difference between log record formats * add more --- src/core/server/logging/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/core/server/logging/README.md b/src/core/server/logging/README.md index 3fbec7a45148da..ed64e7c4ce0b1c 100644 --- a/src/core/server/logging/README.md +++ b/src/core/server/logging/README.md @@ -7,6 +7,8 @@ - [JSON layout](#json-layout) - [Configuration](#configuration) - [Usage](#usage) +- [Logging config migration](#logging-config-migration) +- [Log record format changes](#log-record-format-changes) The way logging works in Kibana is inspired by `log4j 2` logging framework used by [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#logging). The main idea is to have consistent logging behaviour (configuration, log format etc.) across the entire Elastic Stack @@ -321,3 +323,23 @@ Define a custom logger for a specific context. #### logging.filter TBD + +### Log record format changes + +| Parameter | Platform log record in **pattern** format | Legacy Platform log record **text** format | +| --------------- | ------------------------------------------ | ------------------------------------------ | +| @timestamp | ISO8601 `2012-01-31T23:33:22.011Z` | Absolute `23:33:22.011` | +| context | `parent.child` | `['parent', 'child']` | +| level | `DEBUG` | `['debug']` | +| meta | stringified JSON object `{"to": "v8"}` | N/A | +| pid | can be configured as `%pid` | N/A | + +| Parameter | Platform log record in **json** format | Legacy Platform log record **json** format | +| --------------- | ------------------------------------------ | -------------------------------------------- | +| @timestamp | ISO8601_TZ `2012-01-31T23:33:22.011-05:00` | ISO8601 `2012-01-31T23:33:22.011Z` | +| context | `context: parent.child` | `tags: ['parent', 'child']` | +| level | `level: DEBUG` | `tags: ['debug']` | +| meta | separate property `"meta": {"to": "v8"}` | merged in log record `{... "to": "v8"}` | +| pid | `pid: 12345` | `pid: 12345` | +| type | N/A | `type: log` | +| error | `{ message, name, stack }` | `{ message, name, stack, code, signal }` | \ No newline at end of file