From 03920249a64bb4fcd8c8178d75bbce4ec335456e Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Mon, 4 May 2020 19:38:46 -0400 Subject: [PATCH 01/10] [Canvas] Moves canvas Server to NP completely (#65148) * Moves canvas Server to NP completely * Fix typecheck --- .../functions/server/escount.ts | 4 +- .../functions/server/esdocs.ts | 4 +- .../functions/server/essql.ts | 4 +- x-pack/legacy/plugins/canvas/index.js | 3 +- x-pack/legacy/plugins/canvas/init.ts | 16 ----- .../canvas/public/functions/timelion.ts | 2 +- x-pack/legacy/plugins/canvas/server/plugin.ts | 11 ---- x-pack/legacy/plugins/canvas/server/shim.ts | 63 ------------------- .../canvas/server/lib/build_bool_array.js | 0 .../canvas/server/lib/build_es_request.js | 0 .../plugins/canvas/server/lib/filters.js | 0 .../canvas/server/lib/format_response.js | 0 .../canvas/server/lib/get_es_filter.js | 0 .../canvas/server/lib/normalize_type.js | 0 .../plugins/canvas/server/lib/query_es_sql.js | 0 .../canvas/server/lib/sanitize_name.js | 0 .../server/routes/es_fields/es_fields.ts | 2 +- 17 files changed, 12 insertions(+), 97 deletions(-) delete mode 100644 x-pack/legacy/plugins/canvas/init.ts delete mode 100644 x-pack/legacy/plugins/canvas/server/plugin.ts delete mode 100644 x-pack/legacy/plugins/canvas/server/shim.ts rename x-pack/{legacy => }/plugins/canvas/server/lib/build_bool_array.js (100%) rename x-pack/{legacy => }/plugins/canvas/server/lib/build_es_request.js (100%) rename x-pack/{legacy => }/plugins/canvas/server/lib/filters.js (100%) rename x-pack/{legacy => }/plugins/canvas/server/lib/format_response.js (100%) rename x-pack/{legacy => }/plugins/canvas/server/lib/get_es_filter.js (100%) rename x-pack/{legacy => }/plugins/canvas/server/lib/normalize_type.js (100%) rename x-pack/{legacy => }/plugins/canvas/server/lib/query_es_sql.js (100%) rename x-pack/{legacy => }/plugins/canvas/server/lib/sanitize_name.js (100%) diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts index 3f5d0610b4c72..2ab48fe002979 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts @@ -8,8 +8,10 @@ import { ExpressionFunctionDefinition, ExpressionValueFilter, } from 'src/plugins/expressions/common'; +/* eslint-disable */ // @ts-ignore untyped local -import { buildESRequest } from '../../../server/lib/build_es_request'; +import { buildESRequest } from '../../../../../../plugins/canvas/server/lib/build_es_request'; +/* eslint-enable */ import { getFunctionHelp } from '../../../i18n'; interface Arguments { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts index d60297ee2da3f..180afc89322c3 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts @@ -6,8 +6,10 @@ import squel from 'squel'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions'; +/* eslint-disable */ // @ts-ignore untyped local -import { queryEsSQL } from '../../../server/lib/query_es_sql'; +import { queryEsSQL } from '../../../../../../plugins/canvas/server/lib/query_es_sql'; +/* eslint-enable */ import { ExpressionValueFilter } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts index b972f5a3bd4a6..7c9cb92ad009c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts @@ -5,8 +5,10 @@ */ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +/* eslint-disable */ // @ts-ignore untyped local -import { queryEsSQL } from '../../../server/lib/query_es_sql'; +import { queryEsSQL } from '../../../../../../plugins/canvas/server/lib/query_es_sql'; +/* eslint-enable */ import { ExpressionValueFilter } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; diff --git a/x-pack/legacy/plugins/canvas/index.js b/x-pack/legacy/plugins/canvas/index.js index a1d4b35826b00..b62d88c930d91 100644 --- a/x-pack/legacy/plugins/canvas/index.js +++ b/x-pack/legacy/plugins/canvas/index.js @@ -6,7 +6,6 @@ import { resolve } from 'path'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; -import { init } from './init'; import { CANVAS_APP, CANVAS_TYPE, CUSTOM_ELEMENT_TYPE } from './common/lib'; export function canvas(kibana) { @@ -64,6 +63,6 @@ export function canvas(kibana) { }).default(); }, - init, + init: () => undefined, }); } diff --git a/x-pack/legacy/plugins/canvas/init.ts b/x-pack/legacy/plugins/canvas/init.ts deleted file mode 100644 index 89b940a638476..0000000000000 --- a/x-pack/legacy/plugins/canvas/init.ts +++ /dev/null @@ -1,16 +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 { Legacy } from 'kibana'; -import { Plugin } from './server/plugin'; -import { createSetupShim } from './server/shim'; - -export const init = async function(server: Legacy.Server) { - const { coreSetup, pluginsSetup } = await createSetupShim(server); - const serverPlugin = new Plugin(); - - serverPlugin.setup(coreSetup, pluginsSetup); -}; diff --git a/x-pack/legacy/plugins/canvas/public/functions/timelion.ts b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts index d07b3bf6d1d1c..7e38e6e710b81 100644 --- a/x-pack/legacy/plugins/canvas/public/functions/timelion.ts +++ b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts @@ -10,7 +10,7 @@ import { TimeRange } from 'src/plugins/data/common'; import { ExpressionFunctionDefinition, DatatableRow } from 'src/plugins/expressions/public'; import { fetch } from '../../common/lib/fetch'; // @ts-ignore untyped local -import { buildBoolArray } from '../../server/lib/build_bool_array'; +import { buildBoolArray } from '../../public/lib/build_bool_array'; import { Datatable, ExpressionValueFilter } from '../../types'; import { getFunctionHelp } from '../../i18n'; import { InitializeArguments } from './'; diff --git a/x-pack/legacy/plugins/canvas/server/plugin.ts b/x-pack/legacy/plugins/canvas/server/plugin.ts deleted file mode 100644 index 61cb81c91279a..0000000000000 --- a/x-pack/legacy/plugins/canvas/server/plugin.ts +++ /dev/null @@ -1,11 +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 { CoreSetup, PluginsSetup } from './shim'; - -export class Plugin { - public setup(core: CoreSetup, plugins: PluginsSetup) {} -} diff --git a/x-pack/legacy/plugins/canvas/server/shim.ts b/x-pack/legacy/plugins/canvas/server/shim.ts deleted file mode 100644 index c36ee3a291dae..0000000000000 --- a/x-pack/legacy/plugins/canvas/server/shim.ts +++ /dev/null @@ -1,63 +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 { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch'; -import { Legacy } from 'kibana'; - -import { HomeServerPluginSetup } from 'src/plugins/home/server'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { PluginSetupContract } from '../../../../plugins/features/server'; - -export interface CoreSetup { - elasticsearch: ElasticsearchPlugin; - getInjectedUiAppVars: Legacy.Server['getInjectedUiAppVars']; - getServerConfig: Legacy.Server['config']; - http: { - route: Legacy.Server['route']; - }; -} - -export interface PluginsSetup { - features: PluginSetupContract; - home: HomeServerPluginSetup; - interpreter: { - register: (specs: any) => any; - }; - kibana: { - injectedUiAppVars: ReturnType; - }; - usageCollection: UsageCollectionSetup; -} - -export async function createSetupShim( - server: Legacy.Server -): Promise<{ coreSetup: CoreSetup; pluginsSetup: PluginsSetup }> { - const setup = server.newPlatform.setup.core; - return { - coreSetup: { - ...setup, - elasticsearch: server.plugins.elasticsearch, - getInjectedUiAppVars: server.getInjectedUiAppVars, - getServerConfig: () => server.config(), - http: { - // @ts-ignore: New Platform object not typed - ...server.newPlatform.setup.core.http, - route: (...args) => server.route(...args), - }, - }, - pluginsSetup: { - // @ts-ignore: New Platform not typed - features: server.newPlatform.setup.plugins.features, - home: server.newPlatform.setup.plugins.home, - // @ts-ignore Interpreter plugin not typed on legacy server - interpreter: server.plugins.interpreter, - kibana: { - injectedUiAppVars: await server.getInjectedUiAppVars('kibana'), - }, - usageCollection: server.newPlatform.setup.plugins.usageCollection, - }, - }; -} diff --git a/x-pack/legacy/plugins/canvas/server/lib/build_bool_array.js b/x-pack/plugins/canvas/server/lib/build_bool_array.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/build_bool_array.js rename to x-pack/plugins/canvas/server/lib/build_bool_array.js diff --git a/x-pack/legacy/plugins/canvas/server/lib/build_es_request.js b/x-pack/plugins/canvas/server/lib/build_es_request.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/build_es_request.js rename to x-pack/plugins/canvas/server/lib/build_es_request.js diff --git a/x-pack/legacy/plugins/canvas/server/lib/filters.js b/x-pack/plugins/canvas/server/lib/filters.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/filters.js rename to x-pack/plugins/canvas/server/lib/filters.js diff --git a/x-pack/legacy/plugins/canvas/server/lib/format_response.js b/x-pack/plugins/canvas/server/lib/format_response.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/format_response.js rename to x-pack/plugins/canvas/server/lib/format_response.js diff --git a/x-pack/legacy/plugins/canvas/server/lib/get_es_filter.js b/x-pack/plugins/canvas/server/lib/get_es_filter.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/get_es_filter.js rename to x-pack/plugins/canvas/server/lib/get_es_filter.js diff --git a/x-pack/legacy/plugins/canvas/server/lib/normalize_type.js b/x-pack/plugins/canvas/server/lib/normalize_type.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/normalize_type.js rename to x-pack/plugins/canvas/server/lib/normalize_type.js diff --git a/x-pack/legacy/plugins/canvas/server/lib/query_es_sql.js b/x-pack/plugins/canvas/server/lib/query_es_sql.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/query_es_sql.js rename to x-pack/plugins/canvas/server/lib/query_es_sql.js diff --git a/x-pack/legacy/plugins/canvas/server/lib/sanitize_name.js b/x-pack/plugins/canvas/server/lib/sanitize_name.js similarity index 100% rename from x-pack/legacy/plugins/canvas/server/lib/sanitize_name.js rename to x-pack/plugins/canvas/server/lib/sanitize_name.js diff --git a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts index b82f84b931d73..5282a246de6b6 100644 --- a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts +++ b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { API_ROUTE } from '../../../../../legacy/plugins/canvas/common/lib'; import { catchErrorHandler } from '../catch_error_handler'; // @ts-ignore unconverted lib -import { normalizeType } from '../../../../../legacy/plugins/canvas/server/lib/normalize_type'; +import { normalizeType } from '../../lib/normalize_type'; import { RouteInitializerDeps } from '..'; const ESFieldsRequestSchema = schema.object({ From 869f31469788f3e686d234ec876ddba6f8dd473e Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Mon, 4 May 2020 19:39:04 -0400 Subject: [PATCH 02/10] Adds saved objects to Management (#64926) Co-authored-by: Elastic Machine --- .../canvas/server/saved_objects/custom_element.ts | 8 ++++++++ .../plugins/canvas/server/saved_objects/workpad.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/x-pack/plugins/canvas/server/saved_objects/custom_element.ts b/x-pack/plugins/canvas/server/saved_objects/custom_element.ts index 14223455cbc21..d26f6dd8fb16e 100644 --- a/x-pack/plugins/canvas/server/saved_objects/custom_element.ts +++ b/x-pack/plugins/canvas/server/saved_objects/custom_element.ts @@ -30,4 +30,12 @@ export const customElementType: SavedObjectsType = { }, }, migrations: {}, + management: { + icon: 'canvasApp', + defaultSearchField: 'name', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.displayName; + }, + }, }; diff --git a/x-pack/plugins/canvas/server/saved_objects/workpad.ts b/x-pack/plugins/canvas/server/saved_objects/workpad.ts index 918f4bf991076..2e9570b1b83be 100644 --- a/x-pack/plugins/canvas/server/saved_objects/workpad.ts +++ b/x-pack/plugins/canvas/server/saved_objects/workpad.ts @@ -30,4 +30,18 @@ export const workpadType: SavedObjectsType = { migrations: { '7.0.0': removeAttributesId, }, + management: { + importableAndExportable: true, + icon: 'canvasApp', + defaultSearchField: 'name', + getTitle(obj) { + return obj.attributes.name; + }, + getInAppUrl(obj) { + return { + path: `/app/canvas#/workpad/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'canvas.show', + }; + }, + }, }; From bd73d967c91f683f62c625541eadb412f632b405 Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Mon, 4 May 2020 19:40:18 -0400 Subject: [PATCH 03/10] [Canvas] Finish moving UI to Kibana Platform (#64831) * Finish Canvas platform migration * Revert inadvertent change * Import Canvas .scss directly Co-authored-by: Elastic Machine --- x-pack/index.js | 2 - .../plugins/canvas/public/application.tsx | 18 ++++++++- .../canvas/public/components/app/index.js | 3 +- x-pack/legacy/plugins/canvas/public/legacy.ts | 7 ---- .../canvas/public/lib/history_provider.js | 8 +++- .../canvas/public/lib/router_provider.js | 12 ++++++ .../legacy/plugins/canvas/public/plugin.tsx | 37 +++++++------------ .../canvas/public/state/actions/elements.js | 3 +- .../canvas/public/state/actions/workpad.js | 2 +- .../plugins/canvas/public/state/store.js | 9 +++++ x-pack/legacy/plugins/canvas/public/store.ts | 6 ++- .../plugins/canvas/public/style/main.scss | 6 +++ .../transitions/fade/{fade.css => fade.scss} | 0 .../canvas/public/transitions/fade/index.ts | 2 +- .../canvas/public/transitions/rotate/index.ts | 2 +- .../rotate/{rotate.css => rotate.scss} | 0 .../canvas/public/transitions/slide/index.ts | 2 +- .../slide/{slide.css => slide.scss} | 0 .../canvas/public/transitions/zoom/index.ts | 2 +- .../transitions/zoom/{zoom.css => zoom.scss} | 4 +- .../plugins/canvas/shareable_runtime/index.ts | 2 +- x-pack/plugins/canvas/kibana.json | 4 +- x-pack/plugins/canvas/public/index.ts | 10 +++++ 23 files changed, 89 insertions(+), 52 deletions(-) rename x-pack/legacy/plugins/canvas/public/transitions/fade/{fade.css => fade.scss} (100%) rename x-pack/legacy/plugins/canvas/public/transitions/rotate/{rotate.css => rotate.scss} (100%) rename x-pack/legacy/plugins/canvas/public/transitions/slide/{slide.css => slide.scss} (100%) rename x-pack/legacy/plugins/canvas/public/transitions/zoom/{zoom.css => zoom.scss} (77%) create mode 100644 x-pack/plugins/canvas/public/index.ts diff --git a/x-pack/index.js b/x-pack/index.js index 89cbb03f084eb..ab29aaa2a10a2 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -12,7 +12,6 @@ import { dashboardMode } from './legacy/plugins/dashboard_mode'; import { beats } from './legacy/plugins/beats_management'; import { maps } from './legacy/plugins/maps'; import { spaces } from './legacy/plugins/spaces'; -import { canvas } from './legacy/plugins/canvas'; import { infra } from './legacy/plugins/infra'; import { taskManager } from './legacy/plugins/task_manager'; import { encryptedSavedObjects } from './legacy/plugins/encrypted_saved_objects'; @@ -28,7 +27,6 @@ module.exports = function(kibana) { dashboardMode(kibana), beats(kibana), maps(kibana), - canvas(kibana), infra(kibana), taskManager(kibana), encryptedSavedObjects(kibana), diff --git a/x-pack/legacy/plugins/canvas/public/application.tsx b/x-pack/legacy/plugins/canvas/public/application.tsx index f71123cd28b90..8ee65c3386afc 100644 --- a/x-pack/legacy/plugins/canvas/public/application.tsx +++ b/x-pack/legacy/plugins/canvas/public/application.tsx @@ -24,7 +24,7 @@ import { initRegistries, populateRegistries, destroyRegistries } from './registr import { getDocumentationLinks } from './lib/documentation_links'; // @ts-ignore untyped component import { HelpMenu } from './components/help_menu/help_menu'; -import { createStore } from './store'; +import { createStore, destroyStore } from './store'; import { VALUE_CLICK_TRIGGER, ActionByType } from '../../../../../src/plugins/ui_actions/public'; /* eslint-disable */ @@ -35,6 +35,12 @@ import { init as initStatsReporter } from './lib/ui_metric'; import { CapabilitiesStrings } from '../i18n'; import { startServices, stopServices, services } from './services'; +// @ts-ignore Untyped local +import { destroyHistory } from './lib/history_provider'; +// @ts-ignore Untyped local +import { stopRouter } from './lib/router_provider'; + +import './style/index.scss'; const { ReadOnlyBadge: strings } = CapabilitiesStrings; @@ -54,6 +60,8 @@ export const renderApp = ( { element }: AppMountParameters, canvasStore: Store ) => { + element.classList.add('canvas'); + element.classList.add('canvasContainerWrapper'); const canvasServices = Object.entries(services).reduce((reduction, [key, provider]) => { reduction[key] = provider.getService(); @@ -70,7 +78,9 @@ export const renderApp = ( , element ); - return () => ReactDOM.unmountComponentAtNode(element); + return () => { + ReactDOM.unmountComponentAtNode(element); + }; }; export const initializeCanvas = async ( @@ -144,6 +154,7 @@ export const teardownCanvas = (coreStart: CoreStart, startPlugins: CanvasStartDe stopServices(); destroyRegistries(); resetInterpreter(); + destroyStore(); startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, emptyAction.id); if (restoreAction) { @@ -153,4 +164,7 @@ export const teardownCanvas = (coreStart: CoreStart, startPlugins: CanvasStartDe coreStart.chrome.setBadge(undefined); coreStart.chrome.setHelpExtension(undefined); + + destroyHistory(); + stopRouter(); }; diff --git a/x-pack/legacy/plugins/canvas/public/components/app/index.js b/x-pack/legacy/plugins/canvas/public/components/app/index.js index 36af5477631b4..de0d4c190eae6 100644 --- a/x-pack/legacy/plugins/canvas/public/components/app/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/app/index.js @@ -10,7 +10,6 @@ import { getAppReady, getBasePath } from '../../state/selectors/app'; import { appReady, appError } from '../../state/actions/app'; import { App as Component } from './app'; -import { trackRouteChange } from './track_route_change'; const mapStateToProps = state => { // appReady could be an error object @@ -46,6 +45,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { export const App = compose( connect(mapStateToProps, mapDispatchToProps, mergeProps), withProps(() => ({ - onRouteChange: trackRouteChange, + onRouteChange: () => undefined, })) )(Component); diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index f83887bbcbdfd..f4a2b309b3499 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -9,7 +9,6 @@ import { CanvasStartDeps, CanvasSetupDeps } from './plugin'; // eslint-disable-l // @ts-ignore Untyped Kibana Lib import chrome, { loadingCount } from 'ui/chrome'; // eslint-disable-line import/order -import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; // eslint-disable-line import/order // @ts-ignore Untyped Kibana Lib import { formatMsg } from '../../../../../src/plugins/kibana_legacy/public'; // eslint-disable-line import/order @@ -32,12 +31,6 @@ const shimStartPlugins: CanvasStartDeps = { expressions: npStart.plugins.expressions, inspector: npStart.plugins.inspector, uiActions: npStart.plugins.uiActions, - __LEGACY: { - // ToDo: Copy directly into canvas - absoluteToParsedUrl, - // ToDo: Won't be a part of New Platform. Will need to handle internally - trackSubUrlForApp: chrome.trackSubUrlForApp, - }, }; // These methods are intended to be a replacement for import from 'ui/whatever' diff --git a/x-pack/legacy/plugins/canvas/public/lib/history_provider.js b/x-pack/legacy/plugins/canvas/public/lib/history_provider.js index 59b6b88fa38c3..4ff9f0b9d4605 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/history_provider.js +++ b/x-pack/legacy/plugins/canvas/public/lib/history_provider.js @@ -6,7 +6,7 @@ import lzString from 'lz-string'; import { createMemoryHistory, parsePath, createPath } from 'history'; -import createHashStateHistory from 'history-extra'; +import createHashStateHistory from 'history-extra/dist/createHashStateHistory'; import { getWindow } from './get_window'; function wrapHistoryInstance(history) { @@ -134,7 +134,7 @@ function wrapHistoryInstance(history) { return wrappedHistory; } -const instances = new WeakMap(); +let instances = new WeakMap(); const getHistoryInstance = win => { // if no window object, use memory module @@ -158,3 +158,7 @@ export const historyProvider = (win = getWindow()) => { return wrappedInstance; }; + +export const destroyHistory = () => { + instances = new WeakMap(); +}; diff --git a/x-pack/legacy/plugins/canvas/public/lib/router_provider.js b/x-pack/legacy/plugins/canvas/public/lib/router_provider.js index 134875cce681a..42960baa00de1 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/router_provider.js +++ b/x-pack/legacy/plugins/canvas/public/lib/router_provider.js @@ -110,7 +110,19 @@ export function routerProvider(routes) { return unlisten; // return function to remove change handler }, + stop: () => { + for (const listener of componentListeners) { + listener(); + } + }, }; return router; } + +export const stopRouter = () => { + if (router) { + router.stop(); + router = undefined; + } +}; diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx index baeb4ebd453d2..1e85aad6328a5 100644 --- a/x-pack/legacy/plugins/canvas/public/plugin.tsx +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -4,8 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Chrome } from 'ui/chrome'; -import { CoreSetup, CoreStart, Plugin } from '../../../../../src/core/public'; +import { + CoreSetup, + CoreStart, + Plugin, + AppMountParameters, + DEFAULT_APP_CATEGORIES, +} from '../../../../../src/core/public'; import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; import { initLoadingIndicator } from './lib/loading_indicator'; import { featureCatalogueEntry } from './feature_catalogue_entry'; @@ -40,12 +45,7 @@ export interface CanvasStartDeps { embeddable: EmbeddableStart; expressions: ExpressionsStart; inspector: InspectorStart; - uiActions: UiActionsStart; - __LEGACY: { - absoluteToParsedUrl: (url: string, basePath: string) => any; - trackSubUrlForApp: Chrome['trackSubUrlForApp']; - }; } /** @@ -63,7 +63,6 @@ export class CanvasPlugin implements Plugin { // TODO: Do we want to completely move canvas_plugin_src into it's own plugin? private srcPlugin = new CanvasSrcPlugin(); - private startPlugins: CanvasStartDeps | undefined; public setup(core: CoreSetup, plugins: CanvasSetupDeps) { const { api: canvasApi, registries } = getPluginApi(plugins.expressions); @@ -71,28 +70,19 @@ export class CanvasPlugin this.srcPlugin.setup(core, { canvas: canvasApi }); core.application.register({ + category: DEFAULT_APP_CATEGORIES.analyze, id: 'canvas', - title: 'Canvas App', - mount: async (context, params) => { + title: 'Canvas', + euiIconType: 'canvasApp', + order: 0, // need to figure out if this is the proper order for us + mount: async (params: AppMountParameters) => { // Load application bundle const { renderApp, initializeCanvas, teardownCanvas } = await import('./application'); // Get start services const [coreStart, depsStart] = await core.getStartServices(); - // TODO: We only need this to get the __LEGACY stuff that isn't coming from getStartSevices. - // We won't need this as soon as we move over to NP Completely - if (!this.startPlugins) { - throw new Error('Start Plugins not ready at mount time'); - } - - const canvasStore = await initializeCanvas( - core, - coreStart, - plugins, - this.startPlugins, - registries - ); + const canvasStore = await initializeCanvas(core, coreStart, plugins, depsStart, registries); const unmount = renderApp(coreStart, depsStart, params, canvasStore); @@ -127,7 +117,6 @@ export class CanvasPlugin } public start(core: CoreStart, plugins: CanvasStartDeps) { - this.startPlugins = plugins; this.srcPlugin.start(core, plugins); initLoadingIndicator(core.http.addLoadingCountSource); } diff --git a/x-pack/legacy/plugins/canvas/public/state/actions/elements.js b/x-pack/legacy/plugins/canvas/public/state/actions/elements.js index f4a3393b8962d..5ec8eb6137f2b 100644 --- a/x-pack/legacy/plugins/canvas/public/state/actions/elements.js +++ b/x-pack/legacy/plugins/canvas/public/state/actions/elements.js @@ -5,7 +5,7 @@ */ import { createAction } from 'redux-actions'; -import { createThunk } from 'redux-thunks'; +import { createThunk } from 'redux-thunks/cjs'; import immutable from 'object-path-immutable'; import { get, pick, cloneDeep, without } from 'lodash'; import { toExpression, safeElementFromExpression } from '@kbn/interpreter/common'; @@ -116,7 +116,6 @@ export const fetchContext = createThunk( const fetchRenderableWithContextFn = ({ dispatch }, element, ast, context) => { const argumentPath = [element.id, 'expressionRenderable']; - dispatch( args.setLoading({ path: argumentPath, diff --git a/x-pack/legacy/plugins/canvas/public/state/actions/workpad.js b/x-pack/legacy/plugins/canvas/public/state/actions/workpad.js index 5a7fb76ca868c..167c156dce998 100644 --- a/x-pack/legacy/plugins/canvas/public/state/actions/workpad.js +++ b/x-pack/legacy/plugins/canvas/public/state/actions/workpad.js @@ -5,7 +5,7 @@ */ import { createAction } from 'redux-actions'; -import { createThunk } from 'redux-thunks'; +import { createThunk } from 'redux-thunks/cjs'; import { without, includes } from 'lodash'; import { getWorkpadColors } from '../selectors/workpad'; import { fetchAllRenderables } from './elements'; diff --git a/x-pack/legacy/plugins/canvas/public/state/store.js b/x-pack/legacy/plugins/canvas/public/state/store.js index 760e7a8609978..891f30ca4114f 100644 --- a/x-pack/legacy/plugins/canvas/public/state/store.js +++ b/x-pack/legacy/plugins/canvas/public/state/store.js @@ -22,9 +22,18 @@ export function createStore(initialState) { const rootReducer = getRootReducer(initialState); store = createReduxStore(rootReducer, initialState, middleware); + return store; } +export function destroyStore() { + if (store) { + // Replace reducer so that anything that gets fired after navigating away doesn't really do anything + store.replaceReducer(state => state); + } + store = undefined; +} + export function getState() { return store.getState(); } diff --git a/x-pack/legacy/plugins/canvas/public/store.ts b/x-pack/legacy/plugins/canvas/public/store.ts index 0a378979f6ad9..1460932101725 100644 --- a/x-pack/legacy/plugins/canvas/public/store.ts +++ b/x-pack/legacy/plugins/canvas/public/store.ts @@ -5,7 +5,7 @@ */ // @ts-ignore Untyped local -import { createStore as createReduxStore } from './state/store'; +import { createStore as createReduxStore, destroyStore as destroy } from './state/store'; // @ts-ignore Untyped local import { getInitialState } from './state/initial_state'; @@ -29,3 +29,7 @@ export async function createStore(core: CoreSetup, plugins: CanvasSetupDeps) { return createReduxStore(initialState); } + +export function destroyStore() { + destroy(); +} diff --git a/x-pack/legacy/plugins/canvas/public/style/main.scss b/x-pack/legacy/plugins/canvas/public/style/main.scss index 3d41649397190..4387557cbc9f2 100644 --- a/x-pack/legacy/plugins/canvas/public/style/main.scss +++ b/x-pack/legacy/plugins/canvas/public/style/main.scss @@ -3,6 +3,12 @@ */ $canvasElementCardWidth: 210px; + +.canvas.canvasContainerWrapper { + display: flex; + flex-grow: 1; +} + .canvas.canvasContainer { display: flex; flex-grow: 1; diff --git a/x-pack/legacy/plugins/canvas/public/transitions/fade/fade.css b/x-pack/legacy/plugins/canvas/public/transitions/fade/fade.scss similarity index 100% rename from x-pack/legacy/plugins/canvas/public/transitions/fade/fade.css rename to x-pack/legacy/plugins/canvas/public/transitions/fade/fade.scss diff --git a/x-pack/legacy/plugins/canvas/public/transitions/fade/index.ts b/x-pack/legacy/plugins/canvas/public/transitions/fade/index.ts index 71bad81f8b9b8..ad69fae848628 100644 --- a/x-pack/legacy/plugins/canvas/public/transitions/fade/index.ts +++ b/x-pack/legacy/plugins/canvas/public/transitions/fade/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import './fade.css'; +import './fade.scss'; import { TransitionStrings } from '../../../i18n'; diff --git a/x-pack/legacy/plugins/canvas/public/transitions/rotate/index.ts b/x-pack/legacy/plugins/canvas/public/transitions/rotate/index.ts index 0a160d3719290..4a6915e17ee26 100644 --- a/x-pack/legacy/plugins/canvas/public/transitions/rotate/index.ts +++ b/x-pack/legacy/plugins/canvas/public/transitions/rotate/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import './rotate.css'; +import './rotate.scss'; import { TransitionStrings } from '../../../i18n'; diff --git a/x-pack/legacy/plugins/canvas/public/transitions/rotate/rotate.css b/x-pack/legacy/plugins/canvas/public/transitions/rotate/rotate.scss similarity index 100% rename from x-pack/legacy/plugins/canvas/public/transitions/rotate/rotate.css rename to x-pack/legacy/plugins/canvas/public/transitions/rotate/rotate.scss diff --git a/x-pack/legacy/plugins/canvas/public/transitions/slide/index.ts b/x-pack/legacy/plugins/canvas/public/transitions/slide/index.ts index 904acf9f26e76..c0dfb9295c212 100644 --- a/x-pack/legacy/plugins/canvas/public/transitions/slide/index.ts +++ b/x-pack/legacy/plugins/canvas/public/transitions/slide/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import './slide.css'; +import './slide.scss'; import { TransitionStrings } from '../../../i18n'; diff --git a/x-pack/legacy/plugins/canvas/public/transitions/slide/slide.css b/x-pack/legacy/plugins/canvas/public/transitions/slide/slide.scss similarity index 100% rename from x-pack/legacy/plugins/canvas/public/transitions/slide/slide.css rename to x-pack/legacy/plugins/canvas/public/transitions/slide/slide.scss diff --git a/x-pack/legacy/plugins/canvas/public/transitions/zoom/index.ts b/x-pack/legacy/plugins/canvas/public/transitions/zoom/index.ts index 44dc19e6855d2..7aecc56197a6f 100644 --- a/x-pack/legacy/plugins/canvas/public/transitions/zoom/index.ts +++ b/x-pack/legacy/plugins/canvas/public/transitions/zoom/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import './zoom.css'; +import './zoom.scss'; import { TransitionStrings } from '../../../i18n'; diff --git a/x-pack/legacy/plugins/canvas/public/transitions/zoom/zoom.css b/x-pack/legacy/plugins/canvas/public/transitions/zoom/zoom.scss similarity index 77% rename from x-pack/legacy/plugins/canvas/public/transitions/zoom/zoom.css rename to x-pack/legacy/plugins/canvas/public/transitions/zoom/zoom.scss index 6811a6f178907..f76ea71e8d60c 100644 --- a/x-pack/legacy/plugins/canvas/public/transitions/zoom/zoom.css +++ b/x-pack/legacy/plugins/canvas/public/transitions/zoom/zoom.scss @@ -1,7 +1,7 @@ @keyframes zoomIn { from { opacity: 0; - transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(.3, .3, .3); } 50% { @@ -20,7 +20,7 @@ 50% { opacity: 0; - transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(.3, .3, .3); } to { diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/index.ts b/x-pack/legacy/plugins/canvas/shareable_runtime/index.ts index 5f1526d1bc240..2398a348f5a31 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/index.ts +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/index.ts @@ -6,7 +6,7 @@ export * from './api'; import '../../../../../built_assets/css/plugins/kibana/index.light.css'; -import '../../../../../built_assets/css/plugins/canvas/style/index.light.css'; +import '../public/style/index.scss'; import '@elastic/eui/dist/eui_theme_light.css'; import '@kbn/ui-framework/dist/kui_light.css'; diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 3cc442d591f3f..2d6ab43228aa1 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "configPath": ["xpack", "canvas"], "server": true, - "ui": false, - "requiredPlugins": ["expressions", "features", "home"], + "ui": true, + "requiredPlugins": ["data", "embeddable", "expressions", "features", "home", "inspector", "uiActions"], "optionalPlugins": ["usageCollection"] } diff --git a/x-pack/plugins/canvas/public/index.ts b/x-pack/plugins/canvas/public/index.ts new file mode 100644 index 0000000000000..736d5ccc0f8e5 --- /dev/null +++ b/x-pack/plugins/canvas/public/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { PluginInitializerContext } from 'kibana/public'; +import { CanvasPlugin } from '../../../legacy/plugins/canvas/public/plugin'; + +export const plugin = (initializerContext: PluginInitializerContext) => new CanvasPlugin(); From 2d4dc801c30ed5f9aed5a440b68e4c3944f5816f Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 5 May 2020 01:42:58 +0200 Subject: [PATCH 04/10] Update dependency @elastic/charts to v19.1.2 (#64759) * build: upgrade @elastic/charts to 19.0.0 * refactor: onBrushEnd breaking changes * fix: missing onBrushEnd argument changes * fix: update ech to 19.1.1 * fix: lens onBrushEnd breaking changes * update to 19.1.2 --- package.json | 2 +- packages/kbn-ui-shared-deps/package.json | 2 +- .../angular/directives/histogram.tsx | 14 ++-- .../visualizations/views/timeseries/index.js | 10 ++- .../sections/anomalies/chart.tsx | 9 ++- .../sections/log_rate/bar_chart.tsx | 9 ++- .../components/chart_section_vis.tsx | 18 ++++- .../metrics_explorer/components/chart.tsx | 16 ++++- .../plugins/infra/types/eui_experimental.d.ts | 70 ------------------- .../xy_visualization/xy_expression.test.tsx | 2 +- .../public/xy_visualization/xy_expression.tsx | 6 +- .../siem/public/components/charts/common.tsx | 3 +- .../components/matrix_histogram/index.tsx | 10 ++- .../signals_histogram_panel/index.tsx | 3 +- .../signals_histogram.tsx | 4 +- .../detection_engine/detection_engine.tsx | 9 ++- .../detection_engine/rules/details/index.tsx | 9 ++- .../pages/hosts/details/details_tabs.tsx | 9 ++- .../siem/public/pages/hosts/details/index.tsx | 9 ++- .../plugins/siem/public/pages/hosts/hosts.tsx | 9 ++- .../siem/public/pages/hosts/hosts_tabs.tsx | 9 ++- .../network/navigation/network_routes.tsx | 9 ++- .../siem/public/pages/network/network.tsx | 9 ++- .../overview/signals_by_category/index.tsx | 9 ++- .../common/charts/duration_chart.tsx | 16 ++++- .../common/charts/monitor_bar_series.tsx | 7 +- .../common/charts/ping_histogram.tsx | 16 ++++- yarn.lock | 8 +-- 28 files changed, 181 insertions(+), 125 deletions(-) delete mode 100644 x-pack/plugins/infra/types/eui_experimental.d.ts diff --git a/package.json b/package.json index e711235e16ea5..1f0658bd2a138 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "@babel/core": "^7.9.0", "@babel/register": "^7.9.0", "@elastic/apm-rum": "^5.1.1", - "@elastic/charts": "18.4.2", + "@elastic/charts": "19.1.2", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.8.0", "@elastic/eui": "22.3.0", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index ec61e8519c960..a2248f1ae655e 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --watch" }, "dependencies": { - "@elastic/charts": "18.4.2", + "@elastic/charts": "19.1.2", "@elastic/eui": "22.3.0", "@kbn/i18n": "1.0.0", "abortcontroller-polyfill": "^1.4.0", diff --git a/src/plugins/discover/public/application/angular/directives/histogram.tsx b/src/plugins/discover/public/application/angular/directives/histogram.tsx index e7e4cc0ad62f3..d856be58958f1 100644 --- a/src/plugins/discover/public/application/angular/directives/histogram.tsx +++ b/src/plugins/discover/public/application/angular/directives/histogram.tsx @@ -39,6 +39,7 @@ import { TooltipType, ElementClickListener, XYChartElementEvent, + BrushEndListener, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; @@ -143,13 +144,12 @@ export class DiscoverHistogram extends Component { - const range = { - from: min, - to: max, - }; - - this.props.timefilterUpdateHandler(range); + public onBrushEnd: BrushEndListener = ({ x }) => { + if (!x) { + return; + } + const [from, to] = x; + this.props.timefilterUpdateHandler({ from, to }); }; public onElementClick = (xInterval: number): ElementClickListener => ([elementData]) => { diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js index 5cf1619150e5c..874fc037c4896 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js @@ -100,13 +100,21 @@ export const TimeSeries = ({ const { colors } = getChartsSetup(); colors.mappedColors.mapKeys(series.filter(({ color }) => !color).map(({ label }) => label)); + const onBrushEndListener = ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; + onBrush(min, max); + }; + return ( { + const handleBrushEnd = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [startTime, endTime] = x; setTimeRange({ endTime, startTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx index 3a244b6834c59..263c888ca6000 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx @@ -11,6 +11,7 @@ import { niceTimeFormatter, Settings, TooltipValue, + BrushEndListener, LIGHT_THEME, DARK_THEME, } from '@elastic/charts'; @@ -43,8 +44,12 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ [dateFormat] ); - const handleBrushEnd = useCallback( - (startTime: number, endTime: number) => { + const handleBrushEnd = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [startTime, endTime] = x; setTimeRange({ endTime, startTime, diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/chart_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/chart_section_vis.tsx index 588a0d84918c6..7bf5dd6caae48 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/chart_section_vis.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/chart_section_vis.tsx @@ -6,7 +6,15 @@ import React, { useCallback, useMemo } from 'react'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { Axis, Chart, niceTimeFormatter, Position, Settings, TooltipValue } from '@elastic/charts'; +import { + Axis, + Chart, + niceTimeFormatter, + Position, + Settings, + TooltipValue, + BrushEndListener, +} from '@elastic/charts'; import { EuiPageContentBody } from '@elastic/eui'; import { getChartTheme } from '../../metrics_explorer/components/helpers/get_chart_theme'; import { SeriesChart } from './series_chart'; @@ -45,8 +53,12 @@ export const ChartSectionVis = ({ () => (metric != null ? niceTimeFormatter(getMaxMinTimestamp(metric)) : undefined), [metric] ); - const handleTimeChange = useCallback( - (from: number, to: number) => { + const handleTimeChange = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [from, to] = x; if (onChangeRangeTime) { if (isLiveStreaming && stopLiveStreaming) { stopLiveStreaming(); diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx index 089e1abfc4c91..9bd53118cf0ec 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx @@ -7,7 +7,15 @@ import React, { useCallback, useMemo } from 'react'; import { EuiTitle, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Axis, Chart, niceTimeFormatter, Position, Settings, TooltipValue } from '@elastic/charts'; +import { + Axis, + Chart, + niceTimeFormatter, + Position, + Settings, + TooltipValue, + BrushEndListener, +} from '@elastic/charts'; import { first, last } from 'lodash'; import moment from 'moment'; import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer'; @@ -58,7 +66,11 @@ export const MetricsExplorerChart = ({ const isDarkMode = useUiSetting('theme:darkMode'); const { metrics } = options; const [dateFormat] = useKibanaUiSetting('dateFormat'); - const handleTimeChange = (from: number, to: number) => { + const handleTimeChange: BrushEndListener = ({ x }) => { + if (!x) { + return; + } + const [from, to] = x; onTimeChange(moment(from).toISOString(), moment(to).toISOString()); }; const dateFormatter = useMemo( diff --git a/x-pack/plugins/infra/types/eui_experimental.d.ts b/x-pack/plugins/infra/types/eui_experimental.d.ts deleted file mode 100644 index 6b01bd8066adc..0000000000000 --- a/x-pack/plugins/infra/types/eui_experimental.d.ts +++ /dev/null @@ -1,70 +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. - */ - -declare module '@elastic/eui/lib/experimental' { - import { CommonProps } from '@elastic/eui/src/components/common'; - export type EuiSeriesChartProps = CommonProps & { - xType?: string; - stackBy?: string; - statusText?: string; - yDomain?: number[]; - showCrosshair?: boolean; - showDefaultAxis?: boolean; - enableSelectionBrush?: boolean; - crosshairValue?: number; - onSelectionBrushEnd?: (args: any) => void; - onCrosshairUpdate?: (crosshairValue: number) => void; - animateData?: boolean; - marginLeft?: number; - }; - export const EuiSeriesChart: React.FC; - - type EuiSeriesProps = CommonProps & { - data: Array<{ x: number; y: number; y0?: number }>; - lineSize?: number; - name: string; - color?: string; - marginLeft?: number; - }; - export const EuiLineSeries: React.FC; - export const EuiAreaSeries: React.FC; - export const EuiBarSeries: React.FC; - - type EuiYAxisProps = CommonProps & { - tickFormat: (value: number) => string; - marginLeft?: number; - }; - export const EuiYAxis: React.FC; - - type EuiXAxisProps = CommonProps & { - tickFormat?: (value: number) => string; - marginLeft?: number; - }; - export const EuiXAxis: React.FC; - - export interface EuiDataPoint { - seriesIndex: number; - x: number; - y: number; - originalValues: { - x: number; - y: number; - x0?: number; - }; - } - - export interface EuiFormattedValue { - title: any; - value: any; - } - type EuiCrosshairXProps = CommonProps & { - marginLeft?: number; - seriesNames: string[]; - titleFormat?: (dataPoints: EuiDataPoint[]) => EuiFormattedValue | undefined; - itemsFormat?: (dataPoints: EuiDataPoint[]) => EuiFormattedValue[]; - }; - export const EuiCrosshairX: React.FC; -} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx index dd2f32b63e34a..92a09f361230c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -609,7 +609,7 @@ describe('xy_expression', () => { wrapper .find(Settings) .first() - .prop('onBrushEnd')!(1585757732783, 1585758880838); + .prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); expect(executeTriggerActions).toHaveBeenCalledWith('SELECT_RANGE_TRIGGER', { data: { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx index 2cd9ae7acb203..81ae57a5ee638 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -277,7 +277,11 @@ export function XYChart({ theme={chartTheme} rotation={shouldRotate ? 90 : 0} xDomain={xDomain} - onBrushEnd={(min: number, max: number) => { + onBrushEnd={({ x }) => { + if (!x) { + return; + } + const [min, max] = x; // in the future we want to make it also for histogram if (!xAxisColumn || !isTimeViz) { return; diff --git a/x-pack/plugins/siem/public/components/charts/common.tsx b/x-pack/plugins/siem/public/components/charts/common.tsx index 74d728e65f018..7e4b307916042 100644 --- a/x-pack/plugins/siem/public/components/charts/common.tsx +++ b/x-pack/plugins/siem/public/components/charts/common.tsx @@ -15,6 +15,7 @@ import { SettingsSpecProps, TickFormatter, Position, + BrushEndListener, } from '@elastic/charts'; import React, { useMemo } from 'react'; import styled from 'styled-components'; @@ -27,7 +28,7 @@ export const defaultChartWidth = '100%'; const chartDefaultRotation: Rotation = 0; const chartDefaultRendering: Rendering = 'canvas'; -export type UpdateDateRange = (min: number, max: number) => void; +export type UpdateDateRange = BrushEndListener; export interface ChartData { x?: number | string | null; diff --git a/x-pack/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/plugins/siem/public/components/matrix_histogram/index.tsx index 3d4eebd68319c..ba3cb4f62af86 100644 --- a/x-pack/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/siem/public/components/matrix_histogram/index.tsx @@ -108,11 +108,15 @@ export const MatrixHistogramComponent: React.FC { + onBrushEnd: ({ x }) => { + if (!x) { + return; + } + const [from, to] = x; dispatchSetAbsoluteRangeDatePicker({ id: setAbsoluteRangeDatePickerTarget, - from: min, - to: max, + from, + to, }); }, yTickFormatter, diff --git a/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx b/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx index cf6730ea3ec3d..9b336766b1724 100644 --- a/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx +++ b/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx @@ -12,6 +12,7 @@ import { isEmpty } from 'lodash/fp'; import uuid from 'uuid'; import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; +import { UpdateDateRange } from '../../../../components/charts/common'; import { LegendItem } from '../../../../components/charts/draggable_legend_item'; import { escapeDataProviderId } from '../../../../components/drag_and_drop/helpers'; import { HeaderSection } from '../../../../components/header_section'; @@ -70,7 +71,7 @@ interface SignalsHistogramPanelProps { stackByOptions?: SignalsHistogramOption[]; title?: string; to: number; - updateDateRange: (min: number, max: number) => void; + updateDateRange: UpdateDateRange; } const getHistogramOption = (fieldName: string): MatrixHistogramOption => ({ diff --git a/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx b/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx index 4bb7e9f6e122f..a031f2542b877 100644 --- a/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx +++ b/x-pack/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx @@ -15,7 +15,7 @@ import { import { EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { useTheme } from '../../../../components/charts/common'; +import { useTheme, UpdateDateRange } from '../../../../components/charts/common'; import { histogramDateTimeFormatter } from '../../../../components/utils'; import { DraggableLegend } from '../../../../components/charts/draggable_legend'; import { LegendItem } from '../../../../components/charts/draggable_legend_item'; @@ -32,7 +32,7 @@ interface HistogramSignalsProps { loading: boolean; to: number; data: HistogramData[]; - updateDateRange: (min: number, max: number) => void; + updateDateRange: UpdateDateRange; } export const SignalsHistogram = React.memo( ({ diff --git a/x-pack/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/siem/public/pages/detection_engine/detection_engine.tsx index a26d7f5672106..3e23700b08e66 100644 --- a/x-pack/plugins/siem/public/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/siem/public/pages/detection_engine/detection_engine.tsx @@ -13,6 +13,7 @@ import { connect, ConnectedProps } from 'react-redux'; import { GlobalTime } from '../../containers/global_time'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { AlertsTable } from '../../components/alerts_viewer/alerts_table'; +import { UpdateDateRange } from '../../components/charts/common'; import { FiltersGlobal } from '../../components/filters_global'; import { getDetectionEngineTabUrl, @@ -77,8 +78,12 @@ export const DetectionEnginePageComponent: React.FC = ({ const [lastSignals] = useSignalInfo({}); - const updateDateRangeCallback = useCallback( - (min: number, max: number) => { + const updateDateRangeCallback = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index 14e5f2b90882e..3e45c892e23dd 100644 --- a/x-pack/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -22,6 +22,7 @@ import { Redirect, useParams } from 'react-router-dom'; import { StickyContainer } from 'react-sticky'; import { connect, ConnectedProps } from 'react-redux'; +import { UpdateDateRange } from '../../../../components/charts/common'; import { FiltersGlobal } from '../../../../components/filters_global'; import { FormattedDate } from '../../../../components/formatted_date'; import { @@ -209,8 +210,12 @@ export const RuleDetailsPageComponent: FC = ({ signalIndexName, ]); - const updateDateRangeCallback = useCallback( - (min: number, max: number) => { + const updateDateRangeCallback = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/hosts/details/details_tabs.tsx b/x-pack/plugins/siem/public/pages/hosts/details/details_tabs.tsx index f5efd9248029d..d6c0211901ff0 100644 --- a/x-pack/plugins/siem/public/pages/hosts/details/details_tabs.tsx +++ b/x-pack/plugins/siem/public/pages/hosts/details/details_tabs.tsx @@ -7,6 +7,7 @@ import React, { useCallback } from 'react'; import { Route, Switch } from 'react-router-dom'; +import { UpdateDateRange } from '../../../components/charts/common'; import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime'; import { Anomaly } from '../../../components/ml/types'; import { HostsTableType } from '../../../store/hosts/model'; @@ -50,8 +51,12 @@ export const HostDetailsTabs = React.memo( [setAbsoluteRangeDatePicker] ); - const updateDateRange = useCallback( - (min: number, max: number) => { + const updateDateRange = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/plugins/siem/public/pages/hosts/details/index.tsx index ef04288aa1b6f..730c93b43709c 100644 --- a/x-pack/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/plugins/siem/public/pages/hosts/details/index.tsx @@ -9,6 +9,7 @@ import React, { useEffect, useCallback, useMemo } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; +import { UpdateDateRange } from '../../../components/charts/common'; import { FiltersGlobal } from '../../../components/filters_global'; import { HeaderPage } from '../../../components/header_page'; import { LastEventTime } from '../../../components/last_event_time'; @@ -68,8 +69,12 @@ const HostDetailsComponent = React.memo( detailName, ]); const getFilters = () => [...hostDetailsPageFilters, ...filters]; - const narrowDateRange = useCallback( - (min: number, max: number) => { + const narrowDateRange = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/plugins/siem/public/pages/hosts/hosts.tsx index 028c804f4d48f..2fbbc0d96a1e3 100644 --- a/x-pack/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/plugins/siem/public/pages/hosts/hosts.tsx @@ -10,6 +10,7 @@ import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; import { useParams } from 'react-router-dom'; +import { UpdateDateRange } from '../../components/charts/common'; import { FiltersGlobal } from '../../components/filters_global'; import { HeaderPage } from '../../components/header_page'; import { LastEventTime } from '../../components/last_event_time'; @@ -61,8 +62,12 @@ export const HostsComponent = React.memo( } return filters; }, [tabName, filters]); - const narrowDateRange = useCallback( - (min: number, max: number) => { + const narrowDateRange = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/hosts/hosts_tabs.tsx b/x-pack/plugins/siem/public/pages/hosts/hosts_tabs.tsx index 80c35e5563c1d..de25deeb5b477 100644 --- a/x-pack/plugins/siem/public/pages/hosts/hosts_tabs.tsx +++ b/x-pack/plugins/siem/public/pages/hosts/hosts_tabs.tsx @@ -13,6 +13,7 @@ import { Anomaly } from '../../components/ml/types'; import { HostsTableType } from '../../store/hosts/model'; import { AnomaliesQueryTabBody } from '../../containers/anomalies/anomalies_query_tab_body'; import { AnomaliesHostTable } from '../../components/ml/tables/anomalies_host_table'; +import { UpdateDateRange } from '../../components/charts/common'; import { HostsQueryTabBody, @@ -55,8 +56,12 @@ export const HostsTabs = memo( }, [setAbsoluteRangeDatePicker] ), - updateDateRange: useCallback( - (min: number, max: number) => { + updateDateRange: useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/network/navigation/network_routes.tsx b/x-pack/plugins/siem/public/pages/network/navigation/network_routes.tsx index b6b54b68ac06a..fc8b632f87c59 100644 --- a/x-pack/plugins/siem/public/pages/network/navigation/network_routes.tsx +++ b/x-pack/plugins/siem/public/pages/network/navigation/network_routes.tsx @@ -22,6 +22,7 @@ import { NetworkRoutesProps, NetworkRouteType } from './types'; import { TlsQueryTabBody } from './tls_query_tab_body'; import { Anomaly } from '../../../components/ml/types'; import { NetworkAlertsQueryTabBody } from './alerts_query_tab_body'; +import { UpdateDateRange } from '../../../components/charts/common'; export const NetworkRoutes = React.memo( ({ @@ -46,8 +47,12 @@ export const NetworkRoutes = React.memo( }, [setAbsoluteRangeDatePicker] ); - const updateDateRange = useCallback( - (min: number, max: number) => { + const updateDateRange = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/network/network.tsx b/x-pack/plugins/siem/public/pages/network/network.tsx index 65b3142c8c074..698f51efbb451 100644 --- a/x-pack/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/plugins/siem/public/pages/network/network.tsx @@ -11,6 +11,7 @@ import { useParams } from 'react-router-dom'; import { StickyContainer } from 'react-sticky'; import { esQuery } from '../../../../../../src/plugins/data/public'; +import { UpdateDateRange } from '../../components/charts/common'; import { EmbeddedMap } from '../../components/embeddables/embedded_map'; import { FiltersGlobal } from '../../components/filters_global'; import { HeaderPage } from '../../components/header_page'; @@ -61,8 +62,12 @@ const NetworkComponent = React.memo( return filters; }, [tabName, filters]); - const narrowDateRange = useCallback( - (min: number, max: number) => { + const narrowDateRange = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/siem/public/pages/overview/signals_by_category/index.tsx b/x-pack/plugins/siem/public/pages/overview/signals_by_category/index.tsx index 6357d93d7ba71..e5863effa906d 100644 --- a/x-pack/plugins/siem/public/pages/overview/signals_by_category/index.tsx +++ b/x-pack/plugins/siem/public/pages/overview/signals_by_category/index.tsx @@ -14,6 +14,7 @@ import { Filter, IIndexPattern, Query } from '../../../../../../../src/plugins/d import { inputsModel } from '../../../store'; import { InputsModelId } from '../../../store/inputs/constants'; import * as i18n from '../translations'; +import { UpdateDateRange } from '../../../components/charts/common'; const DEFAULT_QUERY: Query = { query: '', language: 'kuery' }; const DEFAULT_STACK_BY = 'signal.rule.threat.tactic.name'; @@ -52,8 +53,12 @@ const SignalsByCategoryComponent: React.FC = ({ to, }) => { const { signalIndexName } = useSignalIndex(); - const updateDateRangeCallback = useCallback( - (min: number, max: number) => { + const updateDateRangeCallback = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; setAbsoluteRangeDatePicker({ id: setAbsoluteRangeDatePickerTarget, from: min, to: max }); }, [setAbsoluteRangeDatePicker] diff --git a/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx index c82b2a1cf9fe2..f9de385a7ce2c 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx @@ -8,7 +8,15 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Axis, Chart, Position, timeFormatter, Settings, SeriesIdentifier } from '@elastic/charts'; +import { + Axis, + Chart, + Position, + timeFormatter, + Settings, + SeriesIdentifier, + BrushEndListener, +} from '@elastic/charts'; import { getChartDateLabel } from '../../../lib/helper'; import { LocationDurationLine } from '../../../../common/types'; import { DurationLineSeriesList } from './duration_line_series_list'; @@ -51,7 +59,11 @@ export const DurationChartComponent = ({ const [hiddenLegends, setHiddenLegends] = useState([]); - const onBrushEnd = (minX: number, maxX: number) => { + const onBrushEnd: BrushEndListener = ({ x }) => { + if (!x) { + return; + } + const [minX, maxX] = x; updateUrlParams({ dateRangeStart: moment(minX).toISOString(), dateRangeEnd: moment(maxX).toISOString(), diff --git a/x-pack/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx b/x-pack/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx index 5e11685e36140..1dccb39b21412 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx @@ -12,6 +12,7 @@ import { Settings, Position, timeFormatter, + BrushEndListener, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; @@ -42,7 +43,11 @@ export const MonitorBarSeries = ({ histogramSeries }: MonitorBarSeriesProps) => const [getUrlParams, updateUrlParams] = useUrlParams(); const { absoluteDateRangeStart, absoluteDateRangeEnd } = getUrlParams(); - const onBrushEnd = (min: number, max: number) => { + const onBrushEnd: BrushEndListener = ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; updateUrlParams({ dateRangeStart: moment(min).toISOString(), dateRangeEnd: moment(max).toISOString(), diff --git a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx index 66e86d6731236..6b86009a6c70a 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx @@ -4,7 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Axis, BarSeries, Chart, Position, Settings, timeFormatter } from '@elastic/charts'; +import { + Axis, + BarSeries, + Chart, + Position, + Settings, + timeFormatter, + BrushEndListener, +} from '@elastic/charts'; import { EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; @@ -79,7 +87,11 @@ export const PingHistogramComponent: React.FC = ({ defaultMessage: 'Up', }); - const onBrushEnd = (min: number, max: number) => { + const onBrushEnd: BrushEndListener = ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; updateUrlParams({ dateRangeStart: moment(min).toISOString(), dateRangeEnd: moment(max).toISOString(), diff --git a/yarn.lock b/yarn.lock index 941143e76483e..7c2282fc2915b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1262,10 +1262,10 @@ dependencies: "@elastic/apm-rum-core" "^5.2.0" -"@elastic/charts@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-18.4.2.tgz#7d3c40dca8a7a701fb7227382191b84d36d8b32a" - integrity sha512-fmEDRUeFEtVWGurafhp/5bHBypOjdXiRXY074tCqnez43hA2iA4v1KrZL8tPFlMePgc/kpZf9wb8aEwxlfWumw== +"@elastic/charts@19.1.2": + version "19.1.2" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-19.1.2.tgz#b78730eb11bdcb4fcf17b213411bc48ae9cb770d" + integrity sha512-Qu4Sgp9Uh5Ic7Te3mCi19wlt8qw9Io8+MmCwpeyUi0TeGCPEIrpHp+aL9JkP+qTQJk+oCrJcjeXo2MhzcwOdCw== dependencies: classnames "^2.2.6" d3-array "^1.2.4" From fb79865aa0342fe9c4d73218d8c41af1eda3c0bb Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 4 May 2020 19:56:38 -0400 Subject: [PATCH 05/10] Import space selector styles (#64656) --- x-pack/plugins/spaces/public/index.scss | 2 -- x-pack/plugins/spaces/public/nav_control/_index.scss | 2 -- .../plugins/spaces/public/nav_control/_nav_control.scss | 3 --- .../spaces/public/nav_control/components/_index.scss | 2 -- ...{_spaces_description.scss => spaces_description.scss} | 0 .../public/nav_control/components/spaces_description.tsx | 1 + .../components/{_spaces_menu.scss => spaces_menu.scss} | 0 .../spaces/public/nav_control/components/spaces_menu.tsx | 1 + .../spaces/public/nav_control/nav_control_popover.tsx | 2 +- x-pack/plugins/spaces/public/space_selector/_index.scss | 2 -- .../spaces/public/space_selector/components/_index.scss | 2 -- .../public/space_selector/components/_space_card.scss | 8 -------- .../public/space_selector/components/space_card.scss | 9 +++++++++ .../public/space_selector/components/space_card.tsx | 1 + .../components/{_space_cards.scss => space_cards.scss} | 0 .../public/space_selector/components/space_cards.tsx | 1 + .../{_space_selector.scss => space_selector.scss} | 0 .../spaces/public/space_selector/space_selector.tsx | 2 ++ 18 files changed, 16 insertions(+), 22 deletions(-) delete mode 100644 x-pack/plugins/spaces/public/nav_control/_index.scss delete mode 100644 x-pack/plugins/spaces/public/nav_control/_nav_control.scss delete mode 100644 x-pack/plugins/spaces/public/nav_control/components/_index.scss rename x-pack/plugins/spaces/public/nav_control/components/{_spaces_description.scss => spaces_description.scss} (100%) rename x-pack/plugins/spaces/public/nav_control/components/{_spaces_menu.scss => spaces_menu.scss} (100%) delete mode 100644 x-pack/plugins/spaces/public/space_selector/_index.scss delete mode 100644 x-pack/plugins/spaces/public/space_selector/components/_index.scss delete mode 100644 x-pack/plugins/spaces/public/space_selector/components/_space_card.scss create mode 100644 x-pack/plugins/spaces/public/space_selector/components/space_card.scss rename x-pack/plugins/spaces/public/space_selector/components/{_space_cards.scss => space_cards.scss} (100%) rename x-pack/plugins/spaces/public/space_selector/{_space_selector.scss => space_selector.scss} (100%) diff --git a/x-pack/plugins/spaces/public/index.scss b/x-pack/plugins/spaces/public/index.scss index 26269f1d31aa3..3f49d85c3db5c 100644 --- a/x-pack/plugins/spaces/public/index.scss +++ b/x-pack/plugins/spaces/public/index.scss @@ -11,6 +11,4 @@ // spcChart__legend-isLoading @import './management/index'; -@import './nav_control/index'; -@import './space_selector/index'; @import './copy_saved_objects_to_space/index'; diff --git a/x-pack/plugins/spaces/public/nav_control/_index.scss b/x-pack/plugins/spaces/public/nav_control/_index.scss deleted file mode 100644 index d0471da325cec..0000000000000 --- a/x-pack/plugins/spaces/public/nav_control/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './components/index'; -@import './nav_control'; \ No newline at end of file diff --git a/x-pack/plugins/spaces/public/nav_control/_nav_control.scss b/x-pack/plugins/spaces/public/nav_control/_nav_control.scss deleted file mode 100644 index 3c08b33cd22d6..0000000000000 --- a/x-pack/plugins/spaces/public/nav_control/_nav_control.scss +++ /dev/null @@ -1,3 +0,0 @@ -.kbnGlobalNavLink__icon .spaceNavGraphic { - margin-top: 0.5em; -} diff --git a/x-pack/plugins/spaces/public/nav_control/components/_index.scss b/x-pack/plugins/spaces/public/nav_control/components/_index.scss deleted file mode 100644 index bbf41993c9aee..0000000000000 --- a/x-pack/plugins/spaces/public/nav_control/components/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './spaces_description'; -@import './spaces_menu'; diff --git a/x-pack/plugins/spaces/public/nav_control/components/_spaces_description.scss b/x-pack/plugins/spaces/public/nav_control/components/spaces_description.scss similarity index 100% rename from x-pack/plugins/spaces/public/nav_control/components/_spaces_description.scss rename to x-pack/plugins/spaces/public/nav_control/components/spaces_description.scss diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx index 3a431ae0929b8..7a73358a6236f 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import './spaces_description.scss'; import { EuiContextMenuPanel, EuiText } from '@elastic/eui'; import React, { FC } from 'react'; import { Capabilities, ApplicationStart } from 'src/core/public'; diff --git a/x-pack/plugins/spaces/public/nav_control/components/_spaces_menu.scss b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.scss similarity index 100% rename from x-pack/plugins/spaces/public/nav_control/components/_spaces_menu.scss rename to x-pack/plugins/spaces/public/nav_control/components/spaces_menu.scss diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx index cdfbc9bbe2683..976b29b0450fb 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import './spaces_menu.scss'; import { EuiContextMenuItem, EuiContextMenuPanel, diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx index 47102cdc79798..7bc0e490bad71 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx @@ -140,7 +140,7 @@ export class NavControlPopover extends Component { } return this.getButton( - , + , (activeSpace as Space).name ); }; diff --git a/x-pack/plugins/spaces/public/space_selector/_index.scss b/x-pack/plugins/spaces/public/space_selector/_index.scss deleted file mode 100644 index 0621aa2a3efd7..0000000000000 --- a/x-pack/plugins/spaces/public/space_selector/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './space_selector'; -@import './components/index'; diff --git a/x-pack/plugins/spaces/public/space_selector/components/_index.scss b/x-pack/plugins/spaces/public/space_selector/components/_index.scss deleted file mode 100644 index b279b7b8b8bed..0000000000000 --- a/x-pack/plugins/spaces/public/space_selector/components/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './space_card'; -@import './space_cards'; diff --git a/x-pack/plugins/spaces/public/space_selector/components/_space_card.scss b/x-pack/plugins/spaces/public/space_selector/components/_space_card.scss deleted file mode 100644 index 81f249c94ff32..0000000000000 --- a/x-pack/plugins/spaces/public/space_selector/components/_space_card.scss +++ /dev/null @@ -1,8 +0,0 @@ -.euiCard.euiCard--isClickable.spaceCard { - width: $euiSizeL * 10; - min-height: 200px; -} - -.spaceCard .euiCard__content{ - overflow: hidden; -} diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_card.scss b/x-pack/plugins/spaces/public/space_selector/components/space_card.scss new file mode 100644 index 0000000000000..7cc46e5863fda --- /dev/null +++ b/x-pack/plugins/spaces/public/space_selector/components/space_card.scss @@ -0,0 +1,9 @@ +.spaceCard { + width: $euiSizeL * 10 !important; + min-height: $euiSize * 12.5; // 200px + + // SASSTODO: Fix EuiCard to truncate or forcewrap long text + .euiCard__content{ + overflow: hidden; + } +} diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx index 7a056427fb166..2e7a30bcb2f77 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import './space_card.scss'; import { EuiCard } from '@elastic/eui'; import React from 'react'; import { addSpaceIdToPath, ENTER_SPACE_PATH } from '../../../common'; diff --git a/x-pack/plugins/spaces/public/space_selector/components/_space_cards.scss b/x-pack/plugins/spaces/public/space_selector/components/space_cards.scss similarity index 100% rename from x-pack/plugins/spaces/public/space_selector/components/_space_cards.scss rename to x-pack/plugins/spaces/public/space_selector/components/space_cards.scss diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx index b480cf524304f..b5500ebc1cd7e 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import './space_cards.scss'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { Component } from 'react'; import { Space } from '../../../common/model/space'; diff --git a/x-pack/plugins/spaces/public/space_selector/_space_selector.scss b/x-pack/plugins/spaces/public/space_selector/space_selector.scss similarity index 100% rename from x-pack/plugins/spaces/public/space_selector/_space_selector.scss rename to x-pack/plugins/spaces/public/space_selector/space_selector.scss diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx index e89d6200a136a..cd15141078897 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './space_selector.scss'; + import { EuiFieldSearch, EuiFlexGroup, From 418804d6ec0335368f27164dece993bdac58bf83 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 4 May 2020 19:59:00 -0400 Subject: [PATCH 06/10] [Ingest] Fix config creation when fleet is not enabled (#65158) --- .../ingest_manager/server/services/agent_config_update.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts b/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts index 8c0e73201e1ff..1cca165906732 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts @@ -7,12 +7,19 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { generateEnrollmentAPIKey, deleteEnrollmentApiKeyForConfigId } from './api_keys'; import { updateAgentsForConfigId, unenrollForConfigId } from './agents'; +import { outputService } from './output'; export async function agentConfigUpdateEventHandler( soClient: SavedObjectsClientContract, action: string, configId: string ) { + const adminUser = await outputService.getAdminUser(soClient); + // If no admin user fleet is not enabled just skip this hook + if (!adminUser) { + return; + } + if (action === 'created') { await generateEnrollmentAPIKey(soClient, { configId, From 6d78489c14fcd2cc353da5d55954b2045b84decd Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 4 May 2020 20:01:40 -0400 Subject: [PATCH 07/10] [Uptime] TLS alerting (#63913) * Refactor settings form event handling and modify certs fields. * Fix/improve broken types/unit/integration/api tests. * Modify default expiration threshold. * Rename test vars. * Implement PR feedback. * Refresh snapshots, fix broken tests/types. * Remove unnecessary state spreading. * Add type for settings field errors. * Refresh test snapshots. * Improve punctuation. * Add TLS alert type. * Add cert API request and runtime type checking. * Add api test for cert api. * Add unload command to certs test. * Extract API params interface to io-ts type. * Add TLS alert type on server. * WIP - add state for changing selected alert type. * Finish adding alert type for client, add server alert summary. * Add some state variables. * Update certs summary function to create required values. * Refresh test snapshots. * Clean up message generator function. * Add a comment. * Update formatting for alert messages, add flags denoting presence of age/expiration data. * Add relative date information to tls alert messages. * Clean up more logic in certs request function. * Fix broken unit tests. * Move tests for common function to new file. * Fix logic error in test and add common state fields to tls alerts. * Extract common state field translations from status check alert. * Add a comment. * Add nested context navigation for uptime alert selection. * Clean up types. * Fix translation key typo. * Extract translations from tls alert factory. * Extract summary messages to translation file. * Change default tls alert time window from 1w to 1d. * Remove unnecessary import. * Simplify page linking. * Extract a non-trivial component to a dedicated file. * Simplify create alert copy. * Fix broken functional test. * Fix busted types. * Fix tls query error. * Allow for alerts toggle button to receive a set of types to display. * Add alerts toggle button to certs page. * Fix copy. * Fixup punctuation in default message to avoid double-period symbols. * Refresh snapshots. --- .../plugins/uptime/common/constants/alerts.ts | 9 + .../plugins/uptime/common/constants/index.ts | 2 +- .../common/runtime_types/alerts/common.ts | 23 +++ .../common/runtime_types/alerts/index.ts | 8 +- .../runtime_types/alerts/status_check.ts | 16 -- .../uptime/common/runtime_types/certs.ts | 2 + .../components/overview/alerts/alert_tls.tsx | 51 +++++ .../alerts/alerts_containers/alert_tls.tsx | 26 +++ .../alerts/alerts_containers/index.ts | 5 +- .../toggle_alert_flyout_button.tsx | 18 +- .../uptime_alerts_flyout_wrapper.tsx | 13 +- .../settings_message_expression_popover.tsx | 72 +++++++ .../alerts/toggle_alert_flyout_button.tsx | 155 ++++++++++----- .../overview/alerts/translations.ts | 79 ++++++++ .../alerts/uptime_alerts_flyout_wrapper.tsx | 6 +- .../overview/overview_container.tsx | 27 +-- .../uptime/public/lib/alert_types/index.ts | 6 +- .../public/lib/alert_types/monitor_status.tsx | 14 +- .../uptime/public/lib/alert_types/tls.tsx | 23 +++ .../public/lib/alert_types/translations.ts | 52 +++++ .../uptime/public/pages/certificates.tsx | 6 +- .../plugins/uptime/public/pages/overview.tsx | 8 +- .../plugins/uptime/public/state/actions/ui.ts | 4 +- .../uptime/public/state/reducers/ui.ts | 7 + .../uptime/public/state/selectors/index.ts | 2 + x-pack/plugins/uptime/public/uptime_app.tsx | 5 +- .../lib/alerts/__tests__/common.test.ts | 180 ++++++++++++++++++ .../lib/alerts/__tests__/status_check.test.ts | 174 ----------------- .../server/lib/alerts/__tests__/tls.test.ts | 147 ++++++++++++++ .../uptime/server/lib/alerts/common.ts | 56 ++++++ .../plugins/uptime/server/lib/alerts/index.ts | 6 +- .../uptime/server/lib/alerts/status_check.ts | 126 +----------- .../plugins/uptime/server/lib/alerts/tls.ts | 152 +++++++++++++++ .../uptime/server/lib/alerts/translations.ts | 150 +++++++++++++++ .../uptime/server/lib/requests/get_certs.ts | 37 +++- .../api_integration/apis/uptime/rest/index.ts | 1 + .../test/functional/services/uptime/alerts.ts | 1 + 37 files changed, 1260 insertions(+), 409 deletions(-) create mode 100644 x-pack/plugins/uptime/common/runtime_types/alerts/common.ts create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/translations.ts create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/tls.tsx create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/translations.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/common.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/tls.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/translations.ts diff --git a/x-pack/plugins/uptime/common/constants/alerts.ts b/x-pack/plugins/uptime/common/constants/alerts.ts index c0db9ae309843..a259fc0a3eb81 100644 --- a/x-pack/plugins/uptime/common/constants/alerts.ts +++ b/x-pack/plugins/uptime/common/constants/alerts.ts @@ -16,4 +16,13 @@ export const ACTION_GROUP_DEFINITIONS: ActionGroupDefinitions = { id: 'xpack.uptime.alerts.actionGroups.monitorStatus', name: 'Uptime Down Monitor', }, + TLS: { + id: 'xpack.uptime.alerts.actionGroups.tls', + name: 'Uptime TLS Alert', + }, +}; + +export const CLIENT_ALERT_TYPES = { + MONITOR_STATUS: 'xpack.uptime.alerts.monitorStatus', + TLS: 'xpack.uptime.alerts.tls', }; diff --git a/x-pack/plugins/uptime/common/constants/index.ts b/x-pack/plugins/uptime/common/constants/index.ts index 00baa39044a55..0ddb995301266 100644 --- a/x-pack/plugins/uptime/common/constants/index.ts +++ b/x-pack/plugins/uptime/common/constants/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ACTION_GROUP_DEFINITIONS } from './alerts'; +export * from './alerts'; export { CHART_FORMAT_LIMITS } from './chart_format_limits'; export { CLIENT_DEFAULTS } from './client_defaults'; export { CONTEXT_DEFAULTS } from './context_defaults'; diff --git a/x-pack/plugins/uptime/common/runtime_types/alerts/common.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/common.ts new file mode 100644 index 0000000000000..a6b03551c9061 --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/alerts/common.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +export const UptimeCommonStateType = t.intersection([ + t.partial({ + currentTriggerStarted: t.string, + firstTriggeredAt: t.string, + lastTriggeredAt: t.string, + lastResolvedAt: t.string, + }), + t.type({ + firstCheckedAt: t.string, + lastCheckedAt: t.string, + isTriggered: t.boolean, + }), +]); + +export type UptimeCommonState = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts index ee284249c38c0..9b2138fc2524c 100644 --- a/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts +++ b/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts @@ -4,9 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - StatusCheckAlertStateType, - StatusCheckAlertState, - StatusCheckExecutorParamsType, - StatusCheckExecutorParams, -} from './status_check'; +export * from './common'; +export * from './status_check'; diff --git a/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts index bc234b268df27..909669bb5d3eb 100644 --- a/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts +++ b/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts @@ -6,22 +6,6 @@ import * as t from 'io-ts'; -export const StatusCheckAlertStateType = t.intersection([ - t.partial({ - currentTriggerStarted: t.string, - firstTriggeredAt: t.string, - lastTriggeredAt: t.string, - lastResolvedAt: t.string, - }), - t.type({ - firstCheckedAt: t.string, - lastCheckedAt: t.string, - isTriggered: t.boolean, - }), -]); - -export type StatusCheckAlertState = t.TypeOf; - export const StatusCheckExecutorParamsType = t.intersection([ t.partial({ filters: t.string, diff --git a/x-pack/plugins/uptime/common/runtime_types/certs.ts b/x-pack/plugins/uptime/common/runtime_types/certs.ts index e9071e76b6d75..923143ca8bb5d 100644 --- a/x-pack/plugins/uptime/common/runtime_types/certs.ts +++ b/x-pack/plugins/uptime/common/runtime_types/certs.ts @@ -15,6 +15,8 @@ export const GetCertsParamsType = t.intersection([ }), t.partial({ search: t.string, + notValidBefore: t.string, + notValidAfter: t.string, from: t.string, to: t.string, }), diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx new file mode 100644 index 0000000000000..203ef07cc2bc8 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx @@ -0,0 +1,51 @@ +/* + * 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 { EuiExpression, EuiFlexItem, EuiFlexGroup, EuiSpacer } from '@elastic/eui'; +import React from 'react'; +import { TlsTranslations } from './translations'; +import { SettingsMessageExpressionPopover } from './settings_message_expression_popover'; + +interface Props { + ageThreshold?: number; + expirationThreshold?: number; + setAlertFlyoutVisible: (value: boolean) => void; +} + +export const AlertTlsComponent: React.FC = props => ( + <> + + + + + + + + + + + + + + +); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx new file mode 100644 index 0000000000000..ab24d0fb5ee67 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useDispatch, useSelector } from 'react-redux'; +import React, { useCallback } from 'react'; +import { AlertTlsComponent } from '../alert_tls'; +import { setAlertFlyoutVisible } from '../../../../state/actions'; +import { selectDynamicSettings } from '../../../../state/selectors'; + +export const AlertTls = () => { + const dispatch = useDispatch(); + const setFlyoutVisible = useCallback((value: boolean) => dispatch(setAlertFlyoutVisible(value)), [ + dispatch, + ]); + const { settings } = useSelector(selectDynamicSettings); + return ( + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts index 87179a96fc0b2..c30312b19f711 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts @@ -5,5 +5,8 @@ */ export { AlertMonitorStatus } from './alert_monitor_status'; -export { ToggleAlertFlyoutButton } from './toggle_alert_flyout_button'; +export { + ToggleAlertFlyoutButton, + ToggleAlertFlyoutButtonProps, +} from './toggle_alert_flyout_button'; export { UptimeAlertsFlyoutWrapper } from './uptime_alerts_flyout_wrapper'; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx index 45ba72d76fba6..c6b2074338c63 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx @@ -6,14 +6,26 @@ import React from 'react'; import { useDispatch } from 'react-redux'; -import { setAlertFlyoutVisible } from '../../../../state/actions'; +import { setAlertFlyoutType, setAlertFlyoutVisible } from '../../../../state/actions'; import { ToggleAlertFlyoutButtonComponent } from '../index'; -export const ToggleAlertFlyoutButton = () => { +export interface ToggleAlertFlyoutButtonProps { + alertOptions?: string[]; +} + +export const ToggleAlertFlyoutButton: React.FC = props => { const dispatch = useDispatch(); return ( dispatch(setAlertFlyoutVisible(value))} + {...props} + setAlertFlyoutVisible={(value: boolean | string) => { + if (typeof value === 'string') { + dispatch(setAlertFlyoutType(value)); + dispatch(setAlertFlyoutVisible(true)); + } else { + dispatch(setAlertFlyoutVisible(value)); + } + }} /> ); }; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx index 7bfd44a762455..33eafbd1e21bc 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx @@ -7,27 +7,22 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { setAlertFlyoutVisible } from '../../../../state/actions'; -import { selectAlertFlyoutVisibility } from '../../../../state/selectors'; -import { UptimeAlertsFlyoutWrapperComponent } from '../index'; +import { UptimeAlertsFlyoutWrapperComponent } from '../uptime_alerts_flyout_wrapper'; +import { selectAlertFlyoutVisibility, selectAlertFlyoutType } from '../../../../state/selectors'; -interface Props { - alertTypeId?: string; - canChangeTrigger?: boolean; -} - -export const UptimeAlertsFlyoutWrapper = ({ alertTypeId, canChangeTrigger }: Props) => { +export const UptimeAlertsFlyoutWrapper: React.FC = () => { const dispatch = useDispatch(); const setAddFlyoutVisibility = (value: React.SetStateAction) => // @ts-ignore the value here is a boolean, and it works with the action creator function dispatch(setAlertFlyoutVisible(value)); const alertFlyoutVisible = useSelector(selectAlertFlyoutVisibility); + const alertTypeId = useSelector(selectAlertFlyoutType); return ( ); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx new file mode 100644 index 0000000000000..8d9de08751eee --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiExpression, EuiPopover } from '@elastic/eui'; +import { Link } from 'react-router-dom'; +import React, { useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { SETTINGS_ROUTE } from '../../../../common/constants'; + +interface SettingsMessageExpressionPopoverProps { + 'aria-label': string; + description: string; + id: string; + setAlertFlyoutVisible: (value: boolean) => void; + value: string; +} + +export const SettingsMessageExpressionPopover: React.FC = ({ + 'aria-label': ariaLabel, + description, + setAlertFlyoutVisible, + value, + id, +}) => { + const [isOpen, setIsOpen] = useState(false); + return ( + setIsOpen(!isOpen)} + value={value} + /> + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + > + + { + setAlertFlyoutVisible(false); + }} + onKeyUp={e => { + if (e.key === 'Enter') { + setAlertFlyoutVisible(false); + } + }} + > + settings page + + + ), + }} + /> + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx index 92fc015a276d3..cba96cd2fe5b1 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -4,27 +4,128 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; +import { + EuiButtonEmpty, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiContextMenuPanelItemDescriptor, + EuiLink, + EuiPopover, +} from '@elastic/eui'; import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { CLIENT_ALERT_TYPES } from '../../../../common/constants'; +import { ToggleFlyoutTranslations } from './translations'; +import { ToggleAlertFlyoutButtonProps } from './alerts_containers'; -interface Props { - setAlertFlyoutVisible: (value: boolean) => void; +interface ComponentProps { + setAlertFlyoutVisible: (value: boolean | string) => void; } -export const ToggleAlertFlyoutButtonComponent = ({ setAlertFlyoutVisible }: Props) => { +type Props = ComponentProps & ToggleAlertFlyoutButtonProps; + +const ALERT_CONTEXT_MAIN_PANEL_ID = 0; +const ALERT_CONTEXT_SELECT_TYPE_PANEL_ID = 1; + +export const ToggleAlertFlyoutButtonComponent: React.FC = ({ + alertOptions, + setAlertFlyoutVisible, +}) => { const [isOpen, setIsOpen] = useState(false); const kibana = useKibana(); + const monitorStatusAlertContextMenuItem: EuiContextMenuPanelItemDescriptor = { + 'aria-label': ToggleFlyoutTranslations.toggleMonitorStatusAriaLabel, + 'data-test-subj': 'xpack.uptime.toggleAlertFlyout', + name: ToggleFlyoutTranslations.toggleMonitorStatusContent, + onClick: () => { + setAlertFlyoutVisible(CLIENT_ALERT_TYPES.MONITOR_STATUS); + setIsOpen(false); + }, + }; + + const tlsAlertContextMenuItem: EuiContextMenuPanelItemDescriptor = { + 'aria-label': ToggleFlyoutTranslations.toggleTlsAriaLabel, + 'data-test-subj': 'xpack.uptime.toggleTlsAlertFlyout', + name: ToggleFlyoutTranslations.toggleTlsContent, + onClick: () => { + setAlertFlyoutVisible(CLIENT_ALERT_TYPES.TLS); + setIsOpen(false); + }, + }; + + const managementContextItem: EuiContextMenuPanelItemDescriptor = { + 'aria-label': ToggleFlyoutTranslations.navigateToAlertingUIAriaLabel, + 'data-test-subj': 'xpack.uptime.navigateToAlertingUi', + name: ( + + + + ), + icon: 'tableOfContents', + }; + + let selectionItems: EuiContextMenuPanelItemDescriptor[] = []; + if (!alertOptions) { + selectionItems = [monitorStatusAlertContextMenuItem, tlsAlertContextMenuItem]; + } else { + alertOptions.forEach(option => { + if (option === CLIENT_ALERT_TYPES.MONITOR_STATUS) + selectionItems.push(monitorStatusAlertContextMenuItem); + else if (option === CLIENT_ALERT_TYPES.TLS) selectionItems.push(tlsAlertContextMenuItem); + }); + } + + if (selectionItems.length === 1) { + selectionItems[0].icon = 'bell'; + } + + let panels: EuiContextMenuPanelDescriptor[]; + if (selectionItems.length === 1) { + panels = [ + { + id: ALERT_CONTEXT_MAIN_PANEL_ID, + title: 'main panel', + items: [...selectionItems, managementContextItem], + }, + ]; + } else { + panels = [ + { + id: ALERT_CONTEXT_MAIN_PANEL_ID, + title: 'main panel', + items: [ + { + 'aria-label': ToggleFlyoutTranslations.openAlertContextPanelAriaLabel, + 'data-test-subj': 'xpack.uptime.openAlertContextPanel', + name: ToggleFlyoutTranslations.openAlertContextPanelLabel, + icon: 'bell', + panel: ALERT_CONTEXT_SELECT_TYPE_PANEL_ID, + }, + managementContextItem, + ], + }, + { + id: ALERT_CONTEXT_SELECT_TYPE_PANEL_ID, + title: 'create alerts', + items: selectionItems, + }, + ]; + } return ( - { - setAlertFlyoutVisible(true); - setIsOpen(false); - }} - > - - , - - - , - ]} - /> + ); }; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts b/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts new file mode 100644 index 0000000000000..406654c808186 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts @@ -0,0 +1,79 @@ +/* + * 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 TlsTranslations = { + criteriaAriaLabel: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.ariaLabel', { + defaultMessage: + 'An expression displaying the criteria for monitor that are watched by this alert', + }), + criteriaDescription: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.description', { + defaultMessage: 'when', + description: + 'The context of this `when` is in the conditional sense, like "when there are three cookies, eat them all".', + }), + criteriaValue: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.value', { + defaultMessage: 'any monitor', + }), + expirationAriaLabel: i18n.translate('xpack.uptime.alerts.tls.expirationExpression.ariaLabel', { + defaultMessage: + 'An expression displaying the threshold that will trigger the TLS alert for certificate expiration', + }), + expirationDescription: i18n.translate( + 'xpack.uptime.alerts.tls.expirationExpression.description', + { + defaultMessage: 'has a certificate expiring within', + } + ), + expirationValue: (value?: number) => + i18n.translate('xpack.uptime.alerts.tls.expirationExpression.value', { + defaultMessage: '{value} days', + values: { value }, + }), + ageAriaLabel: i18n.translate('xpack.uptime.alerts.tls.ageExpression.ariaLabel', { + defaultMessage: + 'An expressing displaying the threshold that will trigger the TLS alert for old certificates', + }), + ageDescription: i18n.translate('xpack.uptime.alerts.tls.ageExpression.description', { + defaultMessage: 'or older than', + }), + ageValue: (value?: number) => + i18n.translate('xpack.uptime.alerts.tls.ageExpression.value', { + defaultMessage: '{value} days', + values: { value }, + }), +}; + +export const ToggleFlyoutTranslations = { + toggleButtonAriaLabel: i18n.translate('xpack.uptime.alertsPopover.toggleButton.ariaLabel', { + defaultMessage: 'Open alert context menu', + }), + openAlertContextPanelAriaLabel: i18n.translate('xpack.uptime.openAlertContextPanel.ariaLabel', { + defaultMessage: 'Open the alert context panel so you can choose an alert type', + }), + openAlertContextPanelLabel: i18n.translate('xpack.uptime.openAlertContextPanel.label', { + defaultMessage: 'Create alert', + }), + toggleTlsAriaLabel: i18n.translate('xpack.uptime.toggleTlsAlertButton.ariaLabel', { + defaultMessage: 'Open TLS alert flyout', + }), + toggleTlsContent: i18n.translate('xpack.uptime.toggleTlsAlertButton.content', { + defaultMessage: 'TLS alert', + }), + toggleMonitorStatusAriaLabel: i18n.translate('xpack.uptime.toggleAlertFlyout.ariaLabel', { + defaultMessage: 'Open add alert flyout', + }), + toggleMonitorStatusContent: i18n.translate('xpack.uptime.toggleAlertButton.content', { + defaultMessage: 'Monitor status alert', + }), + navigateToAlertingUIAriaLabel: i18n.translate('xpack.uptime.navigateToAlertingUi', { + defaultMessage: 'Leave Uptime and go to Alerting Management page', + }), + navigateToAlertingButtonContent: i18n.translate('xpack.uptime.navigateToAlertingButton.content', { + defaultMessage: 'Manage alerts', + }), +}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx index 9b1d3a73dc661..de4e67cfe8edc 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx @@ -10,14 +10,12 @@ import { AlertAdd } from '../../../../../../plugins/triggers_actions_ui/public'; interface Props { alertFlyoutVisible: boolean; alertTypeId?: string; - canChangeTrigger?: boolean; setAlertFlyoutVisibility: React.Dispatch>; } export const UptimeAlertsFlyoutWrapperComponent = ({ alertFlyoutVisible, alertTypeId, - canChangeTrigger, setAlertFlyoutVisibility, }: Props) => ( ); diff --git a/x-pack/plugins/uptime/public/components/overview/overview_container.tsx b/x-pack/plugins/uptime/public/components/overview/overview_container.tsx index d64e489c48076..320536bc63b3c 100644 --- a/x-pack/plugins/uptime/public/components/overview/overview_container.tsx +++ b/x-pack/plugins/uptime/public/components/overview/overview_container.tsx @@ -4,20 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { connect } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import React, { useCallback } from 'react'; +import { DataPublicPluginSetup } from 'src/plugins/data/public'; import { OverviewPageComponent } from '../../pages/overview'; import { selectIndexPattern } from '../../state/selectors'; -import { AppState } from '../../state'; import { setEsKueryString } from '../../state/actions'; -interface DispatchProps { - setEsKueryFilters: typeof setEsKueryString; +export interface OverviewPageProps { + autocomplete: DataPublicPluginSetup['autocomplete']; } -const mapDispatchToProps = (dispatch: any): DispatchProps => ({ - setEsKueryFilters: (esFilters: string) => dispatch(setEsKueryString(esFilters)), -}); - -const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); - -export const OverviewPage = connect(mapStateToProps, mapDispatchToProps)(OverviewPageComponent); +export const OverviewPage: React.FC = props => { + const dispatch = useDispatch(); + const setEsKueryFilters = useCallback( + (esFilters: string) => dispatch(setEsKueryString(esFilters)), + [dispatch] + ); + const indexPattern = useSelector(selectIndexPattern); + return ( + + ); +}; diff --git a/x-pack/plugins/uptime/public/lib/alert_types/index.ts b/x-pack/plugins/uptime/public/lib/alert_types/index.ts index f7ab254ffe675..9a0151e95748c 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/index.ts +++ b/x-pack/plugins/uptime/public/lib/alert_types/index.ts @@ -6,7 +6,11 @@ import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; import { initMonitorStatusAlertType } from './monitor_status'; +import { initTlsAlertType } from './tls'; export type AlertTypeInitializer = (dependenies: { autocomplete: any }) => AlertTypeModel; -export const alertTypeInitializers: AlertTypeInitializer[] = [initMonitorStatusAlertType]; +export const alertTypeInitializers: AlertTypeInitializer[] = [ + initMonitorStatusAlertType, + initTlsAlertType, +]; diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx index e7695fb1cbb56..f93e17270a192 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx @@ -12,6 +12,8 @@ import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; import { AlertTypeInitializer } from '.'; import { StatusCheckExecutorParamsType } from '../../../common/runtime_types'; import { AlertMonitorStatus } from '../../components/overview/alerts/alerts_containers'; +import { CLIENT_ALERT_TYPES } from '../../../common/constants'; +import { MonitorStatusTranslations } from './translations'; export const validate = (alertParams: any) => { const errors: Record = {}; @@ -52,15 +54,15 @@ export const validate = (alertParams: any) => { return { errors }; }; +const { name, defaultActionMessage } = MonitorStatusTranslations; + export const initMonitorStatusAlertType: AlertTypeInitializer = ({ autocomplete, }): AlertTypeModel => ({ - id: 'xpack.uptime.alerts.monitorStatus', - name: 'Uptime monitor status', + id: CLIENT_ALERT_TYPES.MONITOR_STATUS, + name, iconClass: 'uptimeApp', - alertParamsExpression: params => { - return ; - }, + alertParamsExpression: params => , validate, - defaultActionMessage: `{{context.message}}\nLast triggered at: {{state.lastTriggeredAt}}\n{{context.downMonitorsWithGeo}}`, + defaultActionMessage, }); diff --git a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx new file mode 100644 index 0000000000000..0a5c09acbb69f --- /dev/null +++ b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx @@ -0,0 +1,23 @@ +/* + * 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 { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { CLIENT_ALERT_TYPES } from '../../../common/constants'; +import { TlsTranslations } from './translations'; +import { AlertTypeInitializer } from '.'; +import { AlertTls } from '../../components/overview/alerts/alerts_containers/alert_tls'; + +const { name, defaultActionMessage } = TlsTranslations; + +export const initTlsAlertType: AlertTypeInitializer = (): AlertTypeModel => ({ + id: CLIENT_ALERT_TYPES.TLS, + iconClass: 'uptimeApp', + alertParamsExpression: () => , + name, + validate: () => ({ errors: {} }), + defaultActionMessage, +}); diff --git a/x-pack/plugins/uptime/public/lib/alert_types/translations.ts b/x-pack/plugins/uptime/public/lib/alert_types/translations.ts new file mode 100644 index 0000000000000..cdf3cd107b00f --- /dev/null +++ b/x-pack/plugins/uptime/public/lib/alert_types/translations.ts @@ -0,0 +1,52 @@ +/* + * 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 MonitorStatusTranslations = { + defaultActionMessage: i18n.translate('xpack.uptime.alerts.monitorStatus.defaultActionMessage', { + defaultMessage: '{contextMessage}\nLast triggered at: {lastTriggered}\n{downMonitors}', + values: { + contextMessage: '{{context.message}}', + lastTriggered: '{{state.lastTriggeredAt}}', + downMonitors: '{{context.downMonitorsWithGeo}}', + }, + }), + name: i18n.translate('xpack.uptime.alerts.monitorStatus.clientName', { + defaultMessage: 'Uptime monitor status', + }), +}; + +export const TlsTranslations = { + defaultActionMessage: i18n.translate('xpack.uptime.alerts.tls.defaultActionMessage', { + defaultMessage: `Detected {count} TLS certificates expiring or becoming too old. + +{expiringConditionalOpen} +Expiring cert count: {expiringCount} +Expiring Certificates: {expiringCommonNameAndDate} +{expiringConditionalClose} + +{agingConditionalOpen} +Aging cert count: {agingCount} +Aging Certificates: {agingCommonNameAndDate} +{agingConditionalClose} +`, + values: { + count: '{{state.count}}', + expiringCount: '{{state.expiringCount}}', + expiringCommonNameAndDate: '{{state.expiringCommonNameAndDate}}', + expiringConditionalOpen: '{{#state.hasExpired}}', + expiringConditionalClose: '{{/state.hasExpired}}', + agingCount: '{{state.agingCount}}', + agingCommonNameAndDate: '{{state.agingCommonNameAndDate}}', + agingConditionalOpen: '{{#state.hasAging}}', + agingConditionalClose: '{{/state.hasAging}}', + }, + }), + name: i18n.translate('xpack.uptime.alerts.tls.clientName', { + defaultMessage: 'Uptime TLS', + }), +}; diff --git a/x-pack/plugins/uptime/public/pages/certificates.tsx b/x-pack/plugins/uptime/public/pages/certificates.tsx index d6c1b8e2b4568..92a41adcf01cd 100644 --- a/x-pack/plugins/uptime/public/pages/certificates.tsx +++ b/x-pack/plugins/uptime/public/pages/certificates.tsx @@ -19,13 +19,14 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { useTrackPageview } from '../../../observability/public'; import { PageHeader } from './page_header'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; -import { OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../../common/constants'; +import { OVERVIEW_ROUTE, SETTINGS_ROUTE, CLIENT_ALERT_TYPES } from '../../common/constants'; import { getDynamicSettings } from '../state/actions/dynamic_settings'; import { UptimeRefreshContext } from '../contexts'; import * as labels from './translations'; import { UptimePage, useUptimeTelemetry } from '../hooks'; import { certificatesSelector, getCertificatesAction } from '../state/certificates/certificates'; import { CertificateList, CertificateSearch, CertSort } from '../components/certificates'; +import { ToggleAlertFlyoutButton } from '../components/overview/alerts/alerts_containers'; const DEFAULT_PAGE_SIZE = 10; const LOCAL_STORAGE_KEY = 'xpack.uptime.certList.pageSize'; @@ -83,6 +84,9 @@ export const CertificatesPage: React.FC = () => { + + + diff --git a/x-pack/plugins/uptime/public/pages/overview.tsx b/x-pack/plugins/uptime/public/pages/overview.tsx index fefd804cbfabf..65f64aa7352a9 100644 --- a/x-pack/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/plugins/uptime/public/pages/overview.tsx @@ -11,22 +11,20 @@ import { i18n } from '@kbn/i18n'; import { useUptimeTelemetry, UptimePage, useGetUrlParams } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { PageHeader } from './page_header'; -import { DataPublicPluginSetup, IIndexPattern } from '../../../../../src/plugins/data/public'; +import { IIndexPattern } from '../../../../../src/plugins/data/public'; import { useUpdateKueryString } from '../hooks'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { useTrackPageview } from '../../../observability/public'; import { MonitorList } from '../components/overview/monitor_list/monitor_list_container'; import { EmptyState, FilterGroup, KueryBar, ParsingErrorCallout } from '../components/overview'; import { StatusPanel } from '../components/overview/status_panel'; +import { OverviewPageProps } from '../components/overview/overview_container'; -interface OverviewPageProps { - autocomplete: DataPublicPluginSetup['autocomplete']; +interface Props extends OverviewPageProps { indexPattern: IIndexPattern | null; setEsKueryFilters: (esFilters: string) => void; } -type Props = OverviewPageProps; - const EuiFlexItemStyled = styled(EuiFlexItem)` && { min-width: 598px; diff --git a/x-pack/plugins/uptime/public/state/actions/ui.ts b/x-pack/plugins/uptime/public/state/actions/ui.ts index 4885f974dbbd4..80e8796843ac2 100644 --- a/x-pack/plugins/uptime/public/state/actions/ui.ts +++ b/x-pack/plugins/uptime/public/state/actions/ui.ts @@ -12,7 +12,9 @@ export interface PopoverState { export type UiPayload = PopoverState & string & number & Map; -export const setAlertFlyoutVisible = createAction('TOGGLE ALERT FLYOUT'); +export const setAlertFlyoutVisible = createAction('TOGGLE ALERT FLYOUT'); + +export const setAlertFlyoutType = createAction('SET ALERT FLYOUT TYPE'); export const setBasePath = createAction('SET BASE PATH'); diff --git a/x-pack/plugins/uptime/public/state/reducers/ui.ts b/x-pack/plugins/uptime/public/state/reducers/ui.ts index c533f293fc940..82c2bfe2c0cec 100644 --- a/x-pack/plugins/uptime/public/state/reducers/ui.ts +++ b/x-pack/plugins/uptime/public/state/reducers/ui.ts @@ -12,11 +12,13 @@ import { setEsKueryString, triggerAppRefresh, UiPayload, + setAlertFlyoutType, setAlertFlyoutVisible, } from '../actions'; export interface UiState { alertFlyoutVisible: boolean; + alertFlyoutType?: string; basePath: string; esKuery: string; integrationsPopoverOpen: PopoverState | null; @@ -57,6 +59,11 @@ export const uiReducer = handleActions( ...state, esKuery: action.payload as string, }), + + [String(setAlertFlyoutType)]: (state, action: Action) => ({ + ...state, + alertFlyoutType: action.payload, + }), }, initialState ); diff --git a/x-pack/plugins/uptime/public/state/selectors/index.ts b/x-pack/plugins/uptime/public/state/selectors/index.ts index 15fc8b8a7b173..9552538182dd2 100644 --- a/x-pack/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/plugins/uptime/public/state/selectors/index.ts @@ -91,6 +91,8 @@ export const selectDurationLines = ({ monitorDuration }: AppState) => { export const selectAlertFlyoutVisibility = ({ ui: { alertFlyoutVisible } }: AppState) => alertFlyoutVisible; +export const selectAlertFlyoutType = ({ ui: { alertFlyoutType } }: AppState) => alertFlyoutType; + export const selectMonitorStatusAlert = ({ indexPattern, overviewFilters, ui }: AppState) => ({ filters: ui.esKuery, indexPattern: indexPattern.index_pattern, diff --git a/x-pack/plugins/uptime/public/uptime_app.tsx b/x-pack/plugins/uptime/public/uptime_app.tsx index 0d18f959230d1..836d942d92165 100644 --- a/x-pack/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/uptime_app.tsx @@ -105,10 +105,7 @@ const Application = (props: UptimeAppProps) => {
- +
diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts new file mode 100644 index 0000000000000..cd06370816ccc --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts @@ -0,0 +1,180 @@ +/* + * 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 { updateState } from '../common'; + +describe('updateState', () => { + let spy: jest.SpyInstance; + beforeEach(() => { + spy = jest.spyOn(Date.prototype, 'toISOString'); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('sets initial state values', () => { + spy.mockImplementation(() => 'foo date string'); + const result = updateState({}, false); + expect(spy).toHaveBeenCalledTimes(1); + expect(result).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "foo date string", + "firstTriggeredAt": undefined, + "isTriggered": false, + "lastCheckedAt": "foo date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": undefined, + } + `); + }); + + it('updates the correct field in subsequent calls', () => { + spy + .mockImplementationOnce(() => 'first date string') + .mockImplementationOnce(() => 'second date string'); + const firstState = updateState({}, false); + const secondState = updateState(firstState, true); + expect(spy).toHaveBeenCalledTimes(2); + expect(firstState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": undefined, + "isTriggered": false, + "lastCheckedAt": "first date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": undefined, + } + `); + expect(secondState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "second date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": true, + "lastCheckedAt": "second date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "second date string", + } + `); + }); + + it('correctly marks resolution times', () => { + spy + .mockImplementationOnce(() => 'first date string') + .mockImplementationOnce(() => 'second date string') + .mockImplementationOnce(() => 'third date string'); + const firstState = updateState({}, true); + const secondState = updateState(firstState, true); + const thirdState = updateState(secondState, false); + expect(spy).toHaveBeenCalledTimes(3); + expect(firstState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "first date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "first date string", + "isTriggered": true, + "lastCheckedAt": "first date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "first date string", + } + `); + expect(secondState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "first date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "first date string", + "isTriggered": true, + "lastCheckedAt": "second date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "second date string", + } + `); + expect(thirdState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": "first date string", + "isTriggered": false, + "lastCheckedAt": "third date string", + "lastResolvedAt": "third date string", + "lastTriggeredAt": "second date string", + } + `); + }); + + it('correctly marks state fields across multiple triggers/resolutions', () => { + spy + .mockImplementationOnce(() => 'first date string') + .mockImplementationOnce(() => 'second date string') + .mockImplementationOnce(() => 'third date string') + .mockImplementationOnce(() => 'fourth date string') + .mockImplementationOnce(() => 'fifth date string'); + const firstState = updateState({}, false); + const secondState = updateState(firstState, true); + const thirdState = updateState(secondState, false); + const fourthState = updateState(thirdState, true); + const fifthState = updateState(fourthState, false); + expect(spy).toHaveBeenCalledTimes(5); + expect(firstState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": undefined, + "isTriggered": false, + "lastCheckedAt": "first date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": undefined, + } + `); + expect(secondState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "second date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": true, + "lastCheckedAt": "second date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "second date string", + } + `); + expect(thirdState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": false, + "lastCheckedAt": "third date string", + "lastResolvedAt": "third date string", + "lastTriggeredAt": "second date string", + } + `); + expect(fourthState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "fourth date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": true, + "lastCheckedAt": "fourth date string", + "lastResolvedAt": "third date string", + "lastTriggeredAt": "fourth date string", + } + `); + expect(fifthState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": false, + "lastCheckedAt": "fifth date string", + "lastResolvedAt": "fifth date string", + "lastTriggeredAt": "fourth date string", + } + `); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index 24da3f3fa4d06..1cc0f1da820c1 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -7,7 +7,6 @@ import { contextMessage, uniqueMonitorIds, - updateState, statusCheckAlertFactory, fullListByIdAndLocation, } from '../status_check'; @@ -337,179 +336,6 @@ describe('status check alert', () => { }); }); - describe('updateState', () => { - let spy: jest.SpyInstance; - beforeEach(() => { - spy = jest.spyOn(Date.prototype, 'toISOString'); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('sets initial state values', () => { - spy.mockImplementation(() => 'foo date string'); - const result = updateState({}, false); - expect(spy).toHaveBeenCalledTimes(1); - expect(result).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "foo date string", - "firstTriggeredAt": undefined, - "isTriggered": false, - "lastCheckedAt": "foo date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": undefined, - } - `); - }); - - it('updates the correct field in subsequent calls', () => { - spy - .mockImplementationOnce(() => 'first date string') - .mockImplementationOnce(() => 'second date string'); - const firstState = updateState({}, false); - const secondState = updateState(firstState, true); - expect(spy).toHaveBeenCalledTimes(2); - expect(firstState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": undefined, - "isTriggered": false, - "lastCheckedAt": "first date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": undefined, - } - `); - expect(secondState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "second date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": true, - "lastCheckedAt": "second date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "second date string", - } - `); - }); - - it('correctly marks resolution times', () => { - spy - .mockImplementationOnce(() => 'first date string') - .mockImplementationOnce(() => 'second date string') - .mockImplementationOnce(() => 'third date string'); - const firstState = updateState({}, true); - const secondState = updateState(firstState, true); - const thirdState = updateState(secondState, false); - expect(spy).toHaveBeenCalledTimes(3); - expect(firstState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "first date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "first date string", - "isTriggered": true, - "lastCheckedAt": "first date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "first date string", - } - `); - expect(secondState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "first date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "first date string", - "isTriggered": true, - "lastCheckedAt": "second date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "second date string", - } - `); - expect(thirdState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": "first date string", - "isTriggered": false, - "lastCheckedAt": "third date string", - "lastResolvedAt": "third date string", - "lastTriggeredAt": "second date string", - } - `); - }); - - it('correctly marks state fields across multiple triggers/resolutions', () => { - spy - .mockImplementationOnce(() => 'first date string') - .mockImplementationOnce(() => 'second date string') - .mockImplementationOnce(() => 'third date string') - .mockImplementationOnce(() => 'fourth date string') - .mockImplementationOnce(() => 'fifth date string'); - const firstState = updateState({}, false); - const secondState = updateState(firstState, true); - const thirdState = updateState(secondState, false); - const fourthState = updateState(thirdState, true); - const fifthState = updateState(fourthState, false); - expect(spy).toHaveBeenCalledTimes(5); - expect(firstState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": undefined, - "isTriggered": false, - "lastCheckedAt": "first date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": undefined, - } - `); - expect(secondState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "second date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": true, - "lastCheckedAt": "second date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "second date string", - } - `); - expect(thirdState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": false, - "lastCheckedAt": "third date string", - "lastResolvedAt": "third date string", - "lastTriggeredAt": "second date string", - } - `); - expect(fourthState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "fourth date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": true, - "lastCheckedAt": "fourth date string", - "lastResolvedAt": "third date string", - "lastTriggeredAt": "fourth date string", - } - `); - expect(fifthState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": false, - "lastCheckedAt": "fifth date string", - "lastResolvedAt": "fifth date string", - "lastTriggeredAt": "fourth date string", - } - `); - }); - }); - describe('uniqueMonitorIds', () => { let items: GetMonitorStatusResult[]; beforeEach(() => { diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts new file mode 100644 index 0000000000000..2b74d608417cc --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts @@ -0,0 +1,147 @@ +/* + * 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 moment from 'moment'; +import { getCertSummary } from '../tls'; +import { Cert } from '../../../../common/runtime_types'; + +describe('tls alert', () => { + describe('getCertSummary', () => { + let mockCerts: Cert[]; + let diffSpy: jest.SpyInstance; + + beforeEach(() => { + diffSpy = jest.spyOn(moment.prototype, 'diff'); + mockCerts = [ + { + not_after: '2020-07-16T03:15:39.000Z', + not_before: '2019-07-24T03:15:39.000Z', + common_name: 'Common-One', + monitors: [{ name: 'monitor-one', id: 'monitor1' }], + sha256: 'abc', + }, + { + not_after: '2020-07-18T03:15:39.000Z', + not_before: '2019-07-20T03:15:39.000Z', + common_name: 'Common-Two', + monitors: [{ name: 'monitor-two', id: 'monitor2' }], + sha256: 'bcd', + }, + { + not_after: '2020-07-19T03:15:39.000Z', + not_before: '2019-07-22T03:15:39.000Z', + common_name: 'Common-Three', + monitors: [{ name: 'monitor-three', id: 'monitor3' }], + sha256: 'cde', + }, + { + not_after: '2020-07-25T03:15:39.000Z', + not_before: '2019-07-25T03:15:39.000Z', + common_name: 'Common-Four', + monitors: [{ name: 'monitor-four', id: 'monitor4' }], + sha256: 'def', + }, + ]; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('sorts expiring certs appropriately when creating summary', () => { + diffSpy + .mockReturnValueOnce(900) + .mockReturnValueOnce(901) + .mockReturnValueOnce(902); + const result = getCertSummary( + mockCerts, + new Date('2020-07-20T05:00:00.000Z').valueOf(), + new Date('2019-03-01T00:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "", + "agingCount": 0, + "count": 4, + "expiringCommonNameAndDate": "Common-One, expired on 2020-07-16T03:15:39.000Z 900 days ago.; Common-Two, expired on 2020-07-18T03:15:39.000Z 901 days ago.; Common-Three, expired on 2020-07-19T03:15:39.000Z 902 days ago.", + "expiringCount": 3, + "hasAging": null, + "hasExpired": true, + } + `); + }); + + it('sorts aging certs appropriate when creating summary', () => { + diffSpy + .mockReturnValueOnce(702) + .mockReturnValueOnce(701) + .mockReturnValueOnce(700); + const result = getCertSummary( + mockCerts, + new Date('2020-07-01T12:00:00.000Z').valueOf(), + new Date('2019-09-01T03:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "Common-Two, valid since 2019-07-20T03:15:39.000Z, 702 days ago.; Common-Three, valid since 2019-07-22T03:15:39.000Z, 701 days ago.; Common-One, valid since 2019-07-24T03:15:39.000Z, 700 days ago.", + "agingCount": 4, + "count": 4, + "expiringCommonNameAndDate": "", + "expiringCount": 0, + "hasAging": true, + "hasExpired": null, + } + `); + }); + + it('handles negative diff values appropriately for aging certs', () => { + diffSpy + .mockReturnValueOnce(700) + .mockReturnValueOnce(-90) + .mockReturnValueOnce(-80); + const result = getCertSummary( + mockCerts, + new Date('2020-07-01T12:00:00.000Z').valueOf(), + new Date('2019-09-01T03:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "Common-Two, valid since 2019-07-20T03:15:39.000Z, 700 days ago.; Common-Three, invalid until 2019-07-22T03:15:39.000Z, 90 days from now.; Common-One, invalid until 2019-07-24T03:15:39.000Z, 80 days from now.", + "agingCount": 4, + "count": 4, + "expiringCommonNameAndDate": "", + "expiringCount": 0, + "hasAging": true, + "hasExpired": null, + } + `); + }); + + it('handles negative diff values appropriately for expiring certs', () => { + diffSpy + // negative days are in the future, positive days are in the past + .mockReturnValueOnce(-96) + .mockReturnValueOnce(-94) + .mockReturnValueOnce(2); + const result = getCertSummary( + mockCerts, + new Date('2020-07-20T05:00:00.000Z').valueOf(), + new Date('2019-03-01T00:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "", + "agingCount": 0, + "count": 4, + "expiringCommonNameAndDate": "Common-One, expires on 2020-07-16T03:15:39.000Z in 96 days.; Common-Two, expires on 2020-07-18T03:15:39.000Z in 94 days.; Common-Three, expired on 2020-07-19T03:15:39.000Z 2 days ago.", + "expiringCount": 3, + "hasAging": null, + "hasExpired": true, + } + `); + }); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/alerts/common.ts b/x-pack/plugins/uptime/server/lib/alerts/common.ts new file mode 100644 index 0000000000000..f33cd9052d229 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/common.ts @@ -0,0 +1,56 @@ +/* + * 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 { isRight } from 'fp-ts/lib/Either'; +import { UptimeCommonState, UptimeCommonStateType } from '../../../common/runtime_types'; + +export type UpdateUptimeAlertState = ( + state: Record, + isTriggeredNow: boolean +) => UptimeCommonState; + +export const updateState: UpdateUptimeAlertState = (state, isTriggeredNow) => { + const now = new Date().toISOString(); + const decoded = UptimeCommonStateType.decode(state); + if (!isRight(decoded)) { + const triggerVal = isTriggeredNow ? now : undefined; + return { + currentTriggerStarted: triggerVal, + firstCheckedAt: now, + firstTriggeredAt: triggerVal, + isTriggered: isTriggeredNow, + lastTriggeredAt: triggerVal, + lastCheckedAt: now, + lastResolvedAt: undefined, + }; + } + const { + currentTriggerStarted, + firstCheckedAt, + firstTriggeredAt, + lastTriggeredAt, + // this is the stale trigger status, we're naming it `wasTriggered` + // to differentiate it from the `isTriggeredNow` param + isTriggered: wasTriggered, + lastResolvedAt, + } = decoded.right; + + let cts: string | undefined; + if (isTriggeredNow && !currentTriggerStarted) { + cts = now; + } else if (isTriggeredNow) { + cts = currentTriggerStarted; + } + return { + currentTriggerStarted: cts, + firstCheckedAt: firstCheckedAt ?? now, + firstTriggeredAt: isTriggeredNow && !firstTriggeredAt ? now : firstTriggeredAt, + lastCheckedAt: now, + lastTriggeredAt: isTriggeredNow ? now : lastTriggeredAt, + lastResolvedAt: !isTriggeredNow && wasTriggered ? now : lastResolvedAt, + isTriggered: isTriggeredNow, + }; +}; diff --git a/x-pack/plugins/uptime/server/lib/alerts/index.ts b/x-pack/plugins/uptime/server/lib/alerts/index.ts index 0e61fd70e0024..661df39ece628 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/index.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/index.ts @@ -6,5 +6,9 @@ import { UptimeAlertTypeFactory } from './types'; import { statusCheckAlertFactory } from './status_check'; +import { tlsAlertFactory } from './tls'; -export const uptimeAlertTypeFactories: UptimeAlertTypeFactory[] = [statusCheckAlertFactory]; +export const uptimeAlertTypeFactories: UptimeAlertTypeFactory[] = [ + statusCheckAlertFactory, + tlsAlertFactory, +]; diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index f9df559a3977b..0eaa12e8f4372 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -11,13 +11,11 @@ import { i18n } from '@kbn/i18n'; import { AlertExecutorOptions } from '../../../../alerting/server'; import { UptimeAlertTypeFactory } from './types'; import { GetMonitorStatusResult } from '../requests'; -import { - StatusCheckExecutorParamsType, - StatusCheckAlertStateType, - StatusCheckAlertState, -} from '../../../common/runtime_types'; +import { StatusCheckExecutorParamsType } from '../../../common/runtime_types'; import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants'; import { savedObjectsAdapter } from '../saved_objects'; +import { updateState } from './common'; +import { commonStateTranslations } from './translations'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; @@ -34,7 +32,7 @@ export const uniqueMonitorIds = (items: GetMonitorStatusResult[]): Set = /** * Generates a message to include in contexts of alerts. * @param monitors the list of monitors to include in the message - * @param max + * @param max the maximum number of items the summary should contain */ export const contextMessage = (monitorIds: string[], max: number): string => { const MIN = 2; @@ -122,58 +120,11 @@ export const fullListByIdAndLocation = ( ); }; -export const updateState = ( - state: Record, - isTriggeredNow: boolean -): StatusCheckAlertState => { - const now = new Date().toISOString(); - const decoded = StatusCheckAlertStateType.decode(state); - if (!isRight(decoded)) { - const triggerVal = isTriggeredNow ? now : undefined; - return { - currentTriggerStarted: triggerVal, - firstCheckedAt: now, - firstTriggeredAt: triggerVal, - isTriggered: isTriggeredNow, - lastTriggeredAt: triggerVal, - lastCheckedAt: now, - lastResolvedAt: undefined, - }; - } - const { - currentTriggerStarted, - firstCheckedAt, - firstTriggeredAt, - lastTriggeredAt, - // this is the stale trigger status, we're naming it `wasTriggered` - // to differentiate it from the `isTriggeredNow` param - isTriggered: wasTriggered, - lastResolvedAt, - } = decoded.right; - - let cts: string | undefined; - if (isTriggeredNow && !currentTriggerStarted) { - cts = now; - } else if (isTriggeredNow) { - cts = currentTriggerStarted; - } - - return { - currentTriggerStarted: cts, - firstCheckedAt: firstCheckedAt ?? now, - firstTriggeredAt: isTriggeredNow && !firstTriggeredAt ? now : firstTriggeredAt, - lastCheckedAt: now, - lastTriggeredAt: isTriggeredNow ? now : lastTriggeredAt, - lastResolvedAt: !isTriggeredNow && wasTriggered ? now : lastResolvedAt, - isTriggered: isTriggeredNow, - }; -}; - // Right now the maximum number of monitors shown in the message is hardcoded here. // we might want to make this a parameter in the future const DEFAULT_MAX_MESSAGE_ROWS = 3; -export const statusCheckAlertFactory: UptimeAlertTypeFactory = (server, libs) => ({ +export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) => ({ id: 'xpack.uptime.alerts.monitorStatus', name: i18n.translate('xpack.uptime.alerts.monitorStatus', { defaultMessage: 'Uptime monitor status', @@ -218,72 +169,7 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = (server, libs) => ), }, ], - state: [ - { - name: 'firstCheckedAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstCheckedAt', - { - defaultMessage: 'Timestamp indicating when this alert first checked', - } - ), - }, - { - name: 'firstTriggeredAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstTriggeredAt', - { - defaultMessage: 'Timestamp indicating when the alert first triggered', - } - ), - }, - { - name: 'currentTriggerStarted', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.currentTriggerStarted', - { - defaultMessage: - 'Timestamp indicating when the current trigger state began, if alert is triggered', - } - ), - }, - { - name: 'isTriggered', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.isTriggered', - { - defaultMessage: `Flag indicating if the alert is currently triggering`, - } - ), - }, - { - name: 'lastCheckedAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastCheckedAt', - { - defaultMessage: `Timestamp indicating the alert's most recent check time`, - } - ), - }, - { - name: 'lastResolvedAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastResolvedAt', - { - defaultMessage: `Timestamp indicating the most recent resolution time for this alert`, - } - ), - }, - { - name: 'lastTriggeredAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastTriggeredAt', - { - defaultMessage: `Timestamp indicating the alert's most recent trigger time`, - } - ), - }, - ], + state: [...commonStateTranslations], }, async executor(options: AlertExecutorOptions) { const { params: rawParams } = options; diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.ts new file mode 100644 index 0000000000000..518e3ed93b424 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/tls.ts @@ -0,0 +1,152 @@ +/* + * 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 moment from 'moment'; +import { schema } from '@kbn/config-schema'; +import { UptimeAlertTypeFactory } from './types'; +import { savedObjectsAdapter } from '../saved_objects'; +import { updateState } from './common'; +import { ACTION_GROUP_DEFINITIONS, DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; +import { Cert, CertResult } from '../../../common/runtime_types'; +import { commonStateTranslations, tlsTranslations } from './translations'; + +const { TLS } = ACTION_GROUP_DEFINITIONS; + +const DEFAULT_FROM = 'now-1d'; +const DEFAULT_TO = 'now'; +const DEFAULT_INDEX = 0; +const DEFAULT_SIZE = 20; + +interface TlsAlertState { + count: number; + agingCount: number; + agingCommonNameAndDate: string; + expiringCount: number; + expiringCommonNameAndDate: string; + hasAging: true | null; + hasExpired: true | null; +} + +const sortCerts = (a: string, b: string) => new Date(a).valueOf() - new Date(b).valueOf(); + +const mapCertsToSummaryString = ( + certs: Cert[], + certLimitMessage: (cert: Cert) => string, + maxSummaryItems: number +): string => + certs + .slice(0, maxSummaryItems) + .map(cert => `${cert.common_name}, ${certLimitMessage(cert)}`) + .reduce((prev, cur) => (prev === '' ? cur : prev.concat(`; ${cur}`)), ''); + +const getValidAfter = ({ not_after: date }: Cert) => { + if (!date) return 'Error, missing `certificate_not_valid_after` date.'; + const relativeDate = moment().diff(date, 'days'); + return relativeDate >= 0 + ? tlsTranslations.validAfterExpiredString(date, relativeDate) + : tlsTranslations.validAfterExpiringString(date, Math.abs(relativeDate)); +}; + +const getValidBefore = ({ not_before: date }: Cert): string => { + if (!date) return 'Error, missing `certificate_not_valid_before` date.'; + const relativeDate = moment().diff(date, 'days'); + return relativeDate >= 0 + ? tlsTranslations.validBeforeExpiredString(date, relativeDate) + : tlsTranslations.validBeforeExpiringString(date, Math.abs(relativeDate)); +}; + +export const getCertSummary = ( + certs: Cert[], + expirationThreshold: number, + ageThreshold: number, + maxSummaryItems: number = 3 +): TlsAlertState => { + certs.sort((a, b) => sortCerts(a.not_after ?? '', b.not_after ?? '')); + const expiring = certs.filter( + cert => new Date(cert.not_after ?? '').valueOf() < expirationThreshold + ); + + certs.sort((a, b) => sortCerts(a.not_before ?? '', b.not_before ?? '')); + const aging = certs.filter(cert => new Date(cert.not_before ?? '').valueOf() < ageThreshold); + + return { + count: certs.length, + agingCount: aging.length, + agingCommonNameAndDate: mapCertsToSummaryString(aging, getValidBefore, maxSummaryItems), + expiringCommonNameAndDate: mapCertsToSummaryString(expiring, getValidAfter, maxSummaryItems), + expiringCount: expiring.length, + hasAging: aging.length > 0 ? true : null, + hasExpired: expiring.length > 0 ? true : null, + }; +}; + +export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, libs) => ({ + id: 'xpack.uptime.alerts.tls', + name: tlsTranslations.alertFactoryName, + validate: { + params: schema.object({}), + }, + defaultActionGroupId: TLS.id, + actionGroups: [ + { + id: TLS.id, + name: TLS.name, + }, + ], + actionVariables: { + context: [], + state: [...tlsTranslations.actionVariables, ...commonStateTranslations], + }, + async executor(options) { + const { + services: { alertInstanceFactory, callCluster, savedObjectsClient }, + state, + } = options; + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); + + const { certs, total }: CertResult = await libs.requests.getCerts({ + callES: callCluster, + dynamicSettings, + from: DEFAULT_FROM, + to: DEFAULT_TO, + index: DEFAULT_INDEX, + size: DEFAULT_SIZE, + notValidAfter: `now+${dynamicSettings.certThresholds?.expiration ?? + DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.expiration}d`, + notValidBefore: `now-${dynamicSettings.certThresholds?.age ?? + DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.age}d`, + sortBy: 'common_name', + direction: 'desc', + }); + + const foundCerts = total > 0; + + if (foundCerts) { + const absoluteExpirationThreshold = moment() + .add( + dynamicSettings.certThresholds?.expiration ?? + DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.expiration, + 'd' + ) + .valueOf(); + const absoluteAgeThreshold = moment() + .subtract( + dynamicSettings.certThresholds?.age ?? DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.age, + 'd' + ) + .valueOf(); + const alertInstance = alertInstanceFactory(TLS.id); + const summary = getCertSummary(certs, absoluteExpirationThreshold, absoluteAgeThreshold); + alertInstance.replaceState({ + ...updateState(state, foundCerts), + ...summary, + }); + alertInstance.scheduleActions(TLS.id); + } + + return updateState(state, foundCerts); + }, +}); diff --git a/x-pack/plugins/uptime/server/lib/alerts/translations.ts b/x-pack/plugins/uptime/server/lib/alerts/translations.ts new file mode 100644 index 0000000000000..e41930aad5af0 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/translations.ts @@ -0,0 +1,150 @@ +/* + * 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 commonStateTranslations = [ + { + name: 'firstCheckedAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstCheckedAt', + { + defaultMessage: 'Timestamp indicating when this alert first checked', + } + ), + }, + { + name: 'firstTriggeredAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstTriggeredAt', + { + defaultMessage: 'Timestamp indicating when the alert first triggered', + } + ), + }, + { + name: 'currentTriggerStarted', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.currentTriggerStarted', + { + defaultMessage: + 'Timestamp indicating when the current trigger state began, if alert is triggered', + } + ), + }, + { + name: 'isTriggered', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.isTriggered', + { + defaultMessage: `Flag indicating if the alert is currently triggering`, + } + ), + }, + { + name: 'lastCheckedAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastCheckedAt', + { + defaultMessage: `Timestamp indicating the alert's most recent check time`, + } + ), + }, + { + name: 'lastResolvedAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastResolvedAt', + { + defaultMessage: `Timestamp indicating the most recent resolution time for this alert`, + } + ), + }, + { + name: 'lastTriggeredAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastTriggeredAt', + { + defaultMessage: `Timestamp indicating the alert's most recent trigger time`, + } + ), + }, +]; + +export const tlsTranslations = { + alertFactoryName: i18n.translate('xpack.uptime.alerts.tls', { + defaultMessage: 'Uptime TLS', + }), + actionVariables: [ + { + name: 'count', + description: i18n.translate('xpack.uptime.alerts.tls.actionVariables.state.count', { + defaultMessage: 'The number of certs detected by the alert executor', + }), + }, + { + name: 'expiringCount', + description: i18n.translate('xpack.uptime.alerts.tls.actionVariables.state.expiringCount', { + defaultMessage: 'The number of expiring certs detected by the alert.', + }), + }, + { + name: 'expiringCommonNameAndDate', + description: i18n.translate( + 'xpack.uptime.alerts.tls.actionVariables.state.expiringCommonNameAndDate', + { + defaultMessage: 'The common names and expiration date/time of the detected certs', + } + ), + }, + { + name: 'agingCount', + description: i18n.translate('xpack.uptime.alerts.tls.actionVariables.state.agingCount', { + defaultMessage: 'The number of detected certs that are becoming too old.', + }), + }, + { + name: 'agingCommonNameAndDate', + description: i18n.translate( + 'xpack.uptime.alerts.tls.actionVariables.state.agingCommonNameAndDate', + { + defaultMessage: 'The common names and expiration date/time of the detected certs.', + } + ), + }, + ], + validAfterExpiredString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validAfterExpiredString', { + defaultMessage: `expired on {date} {relativeDate} days ago.`, + values: { + date, + relativeDate, + }, + }), + validAfterExpiringString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validAfterExpiringString', { + defaultMessage: `expires on {date} in {relativeDate} days.`, + values: { + date, + relativeDate, + }, + }), + validBeforeExpiredString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validBeforeExpiredString', { + defaultMessage: 'valid since {date}, {relativeDate} days ago.', + values: { + date, + relativeDate, + }, + }), + validBeforeExpiringString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validBeforeExpiringString', { + defaultMessage: 'invalid until {date}, {relativeDate} days from now.', + values: { + date, + relativeDate, + }, + }), +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts index 6820cd69376d1..57a59936ddf7c 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts @@ -20,8 +20,10 @@ export const getCerts: UMElasticsearchQueryFn = asyn index, from, to, - search, size, + search, + notValidBefore, + notValidAfter, sortBy, direction, }) => { @@ -91,6 +93,10 @@ export const getCerts: UMElasticsearchQueryFn = asyn }, }; + if (!params.body.query.bool.should) { + params.body.query.bool.should = []; + } + if (search) { params.body.query.bool.minimum_should_match = 1; params.body.query.bool.should = [ @@ -110,6 +116,35 @@ export const getCerts: UMElasticsearchQueryFn = asyn ]; } + if (notValidBefore || notValidAfter) { + const validityFilters: any = { + bool: { + should: [], + }, + }; + if (notValidBefore) { + validityFilters.bool.should.push({ + range: { + 'tls.certificate_not_valid_before': { + lte: notValidBefore, + }, + }, + }); + } + if (notValidAfter) { + validityFilters.bool.should.push({ + range: { + 'tls.certificate_not_valid_after': { + lte: notValidAfter, + }, + }, + }); + } + + params.body.query.bool.filter.push(validityFilters); + } + + // console.log(JSON.stringify(params, null, 2)); const result = await callES('search', params); const certs = (result?.hits?.hits ?? []).map((hit: any) => { diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index f77c14e960ab2..2084c1a572058 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -46,6 +46,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./monitor_states_generated')); loadTestFile(require.resolve('./telemetry_collectors')); }); + describe('with real-world data', () => { beforeEach('load heartbeat data', async () => await esArchiver.load('uptime/full_heartbeat')); afterEach('unload', async () => await esArchiver.unload('uptime/full_heartbeat')); diff --git a/x-pack/test/functional/services/uptime/alerts.ts b/x-pack/test/functional/services/uptime/alerts.ts index 3a8193ff3d327..03620621d68f1 100644 --- a/x-pack/test/functional/services/uptime/alerts.ts +++ b/x-pack/test/functional/services/uptime/alerts.ts @@ -13,6 +13,7 @@ export function UptimeAlertsProvider({ getService }: FtrProviderContext) { return { async openFlyout() { await testSubjects.click('xpack.uptime.alertsPopover.toggleButton', 5000); + await testSubjects.click('xpack.uptime.openAlertContextPanel', 5000); await testSubjects.click('xpack.uptime.toggleAlertFlyout', 5000); }, async openMonitorStatusAlertType(alertType: string) { From 86332e2bb89e3dde5d2d4a790fc29b757b13ffc4 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 4 May 2020 17:07:44 -0700 Subject: [PATCH 08/10] Fix password field showing as null instead of empty string (#65183) --- .../server/services/epm/agent/agent.test.ts | 9 +++++++++ .../server/services/epm/agent/agent.ts | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts index db2e4fe474640..a63feda504e95 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts @@ -17,9 +17,14 @@ describe('createStream', () => { exclude_files: [".gz$"] processors: - add_locale: ~ + password: {{password}} + {{#if password}} + hidden_password: {{password}} + {{/if}} `; const vars = { paths: { value: ['/usr/local/var/log/nginx/access.log'] }, + password: { type: 'password', value: '' }, }; const output = createStream(vars, streamTemplate); @@ -28,6 +33,7 @@ describe('createStream', () => { paths: ['/usr/local/var/log/nginx/access.log'], exclude_files: ['.gz$'], processors: [{ add_locale: null }], + password: '', }); }); @@ -36,6 +42,7 @@ describe('createStream', () => { input: redis/metrics metricsets: ["key"] test: null + password: {{password}} {{#if key.patterns}} key.patterns: {{key.patterns}} {{/if}} @@ -48,6 +55,7 @@ describe('createStream', () => { pattern: '*' `, }, + password: { type: 'password', value: '' }, }; const output = createStream(vars, streamTemplate); @@ -61,6 +69,7 @@ describe('createStream', () => { pattern: '*', }, ], + password: '', }); }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts index 8254c0d8aaa37..35411a2e95acf 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts @@ -71,5 +71,16 @@ export function createStream(variables: DatasourceConfigRecord, streamTemplate: const stream = template(vars); const yamlFromStream = safeLoad(stream, {}); - return replaceVariablesInYaml(yamlValues, yamlFromStream); + // Hack to keep empty string ('') values around in the end yaml because + // `safeLoad` replaces empty strings with null + const patchedYamlFromStream = Object.entries(yamlFromStream).reduce((acc, [key, value]) => { + if (value === null && typeof vars[key] === 'string' && vars[key].trim() === '') { + acc[key] = ''; + } else { + acc[key] = value; + } + return acc; + }, {} as { [k: string]: any }); + + return replaceVariablesInYaml(yamlValues, patchedYamlFromStream); } From bab70fbd88dbe0a3c24f516a29e96f37504207ed Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Mon, 4 May 2020 20:16:19 -0400 Subject: [PATCH 09/10] add updates available to overview (#65193) --- .../components/integration_section.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx index f4c122af88371..33db53e6fbff4 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx @@ -22,10 +22,11 @@ import { InstallationStatus } from '../../../types'; export const OverviewIntegrationSection: React.FC = () => { const packagesRequest = useGetPackages(); - - const total = packagesRequest.data?.response?.length ?? 0; - const installed = - packagesRequest.data?.response?.filter(p => p.status === InstallationStatus.installed) + const res = packagesRequest.data?.response; + const total = res?.length ?? 0; + const installed = res?.filter(p => p.status === InstallationStatus.installed)?.length ?? 0; + const updatablePackages = + res?.filter(item => 'savedObject' in item && item.version > item.savedObject.attributes.version) ?.length ?? 0; return ( @@ -69,6 +70,15 @@ export const OverviewIntegrationSection: React.FC = () => { + + + + + + )} From fc6c78f53e832d58c0f1ae0369787583ba24f34a Mon Sep 17 00:00:00 2001 From: Henry Harding Date: Mon, 4 May 2020 21:06:09 -0400 Subject: [PATCH 10/10] remove beaker icon, show text instead (#65153) --- .../applications/ingest_manager/sections/overview/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx index 3cd778fb4f016..da77b5be284bc 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx @@ -51,7 +51,6 @@ export const IngestManagerOverview: React.FunctionComponent = () => { defaultMessage="Ingest Manager" />