diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc new file mode 100644 index 00000000000000..b520cc46bef8d3 --- /dev/null +++ b/docs/apm/api.asciidoc @@ -0,0 +1,260 @@ +[role="xpack"] +[[apm-api]] +== API + +Some APM app features are provided via a REST API: + +* <> + +TIP: Kibana provides additional <>, +and general information on <>. + +//// +******************************************************* +//// + +[[agent-config-api]] +=== Agent Configuration API + +The Agent configuration API allows you to fine-tune your APM agent configuration, +without needing to redeploy your application. + +The following Agent configuration APIs are available: + +* <> to create or update an Agent configuration +* <> to delete an Agent configuration. +* <> to list all Agent configurations. +* <> to search for an Agent configuration. + +//// +******************************************************* +//// + +[[apm-update-config]] +==== Create or update configuration + +[[apm-update-config-req]] +===== Request + +`PUT /api/apm/settings/agent-configuration` + +[[apm-update-config-req-body]] +===== Request body + +`service`:: +(required, object) Service identifying the configuration to create or update. + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service + +`settings`:: +(required) Key/value object with settings and their corresponding value. + +`agent_name`:: +(optional) The agent name is used by the UI to determine which settings to display. + + +[[apm-update-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +PUT /api/apm/settings/agent-configuration +{ + "service" : { + "name" : "frontend", + "environment" : "production" + }, + "settings" : { + "transaction_sample_rate" : 0.4, + "capture_body" : "off", + "transaction_max_spans" : 500 + }, + "agent_name": "nodejs" +} +-------------------------------------------------- + +//// +******************************************************* +//// + + +[[apm-delete-config]] +==== Delete configuration + +[[apm-delete-config-req]] +===== Request + +`DELETE /api/apm/settings/agent-configuration` + +[[apm-delete-config-req-body]] +===== Request body +`service`:: +(required, object) Service identifying the configuration to delete + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service + + +[[apm-delete-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +DELETE /api/apm/settings/agent-configuration +{ + "service" : { + "name" : "frontend", + "environment": "production" + } +} +-------------------------------------------------- + +//// +******************************************************* +//// + + +[[apm-list-config]] +==== List configuration + + +[[apm-list-config-req]] +===== Request + +`GET /api/apm/settings/agent-configuration` + +[[apm-list-config-body]] +===== Response body + +[source,js] +-------------------------------------------------- +[ + { + "agent_name": "go", + "service": { + "name": "opbeans-go", + "environment": "production" + }, + "settings": { + "transaction_sample_rate": 1, + "capture_body": "off", + "transaction_max_spans": 200 + }, + "@timestamp": 1581934104843, + "applied_by_agent": false, + "etag": "1e58c178efeebae15c25c539da740d21dee422fc" + }, + { + "agent_name": "go", + "service": { + "name": "opbeans-go" + }, + "settings": { + "transaction_sample_rate": 1, + "capture_body": "off", + "transaction_max_spans": 300 + }, + "@timestamp": 1581934111727, + "applied_by_agent": false, + "etag": "3eed916d3db434d9fb7f039daa681c7a04539a64" + }, + { + "agent_name": "nodejs", + "service": { + "name": "frontend" + }, + "settings": { + "transaction_sample_rate": 1, + }, + "@timestamp": 1582031336265, + "applied_by_agent": false, + "etag": "5080ed25785b7b19f32713681e79f46996801a5b" + } +] +-------------------------------------------------- + +[[apm-list-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +GET /api/apm/settings/agent-configuration +-------------------------------------------------- + +//// +******************************************************* +//// + + +[[apm-search-config]] +==== Search configuration + +[[apm-search-config-req]] +===== Request + +`POST /api/apm/settings/agent-configuration/search` + +[[apm-search-config-req-body]] +===== Request body + +`service`:: +(required, object) Service identifying the configuration. + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service + +`etag`:: +(required) etag is sent by the agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`. + +[[apm-search-config-body]] +===== Response body + +[source,js] +-------------------------------------------------- +{ + "_index": ".apm-agent-configuration", + "_id": "CIaqXXABmQCdPphWj8EJ", + "_score": 2, + "_source": { + "agent_name": "nodejs", + "service": { + "name": "frontend" + }, + "settings": { + "transaction_sample_rate": 1, + }, + "@timestamp": 1582031336265, + "applied_by_agent": false, + "etag": "5080ed25785b7b19f32713681e79f46996801a5b" + } +} +-------------------------------------------------- + +[[apm-search-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +POST /api/apm/settings/agent-configuration/search +{ + "etag" : "1e58c178efeebae15c25c539da740d21dee422fc", + "service" : { + "name" : "frontend", + "environment": "production" + } +} +-------------------------------------------------- + +//// +******************************************************* +//// diff --git a/docs/apm/index.asciidoc b/docs/apm/index.asciidoc index 0c028f64603d81..b6defdeed28429 100644 --- a/docs/apm/index.asciidoc +++ b/docs/apm/index.asciidoc @@ -30,3 +30,5 @@ include::getting-started.asciidoc[] include::bottlenecks.asciidoc[] include::using-the-apm-ui.asciidoc[] + +include::api.asciidoc[] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index ef371bcf24dc29..b8302898d580e4 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -238,7 +238,7 @@ This setting does not impact <> and <> visualizations. Supported on {ece}. Each layer object points to an external vector file that contains a geojson FeatureCollection. The file must use the -https://en.wikipedia.org/wiki/World_Geodetic_System[WGS84 coordinate reference system] +https://en.wikipedia.org/wiki/World_Geodetic_System[WGS84 coordinate reference system (ESPG:4326)] and only include polygons. If the file is hosted on a separate domain from Kibana, the server needs to be CORS-enabled so Kibana can download the file. The following example shows a valid regionmap configuration. diff --git a/examples/demo_search/common/index.ts b/examples/demo_search/common/index.ts index 6587ee96ef61ba..8ea8d6186ee82c 100644 --- a/examples/demo_search/common/index.ts +++ b/examples/demo_search/common/index.ts @@ -20,6 +20,7 @@ import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../../src/plugins/data/public'; export const DEMO_SEARCH_STRATEGY = 'DEMO_SEARCH_STRATEGY'; +export const ASYNC_DEMO_SEARCH_STRATEGY = 'ASYNC_DEMO_SEARCH_STRATEGY'; export interface IDemoRequest extends IKibanaSearchRequest { mood: string | 'sad' | 'happy'; @@ -29,3 +30,11 @@ export interface IDemoRequest extends IKibanaSearchRequest { export interface IDemoResponse extends IKibanaSearchResponse { greeting: string; } + +export interface IAsyncDemoRequest extends IKibanaSearchRequest { + fibonacciNumbers: number; +} + +export interface IAsyncDemoResponse extends IKibanaSearchResponse { + fibonacciSequence: number[]; +} diff --git a/examples/demo_search/kibana.json b/examples/demo_search/kibana.json index 0603706b07d1fe..cb73274ed23f7e 100644 --- a/examples/demo_search/kibana.json +++ b/examples/demo_search/kibana.json @@ -2,7 +2,7 @@ "id": "demoSearch", "version": "0.0.1", "kibanaVersion": "kibana", - "configPath": ["demo_search"], + "configPath": ["demoSearch"], "server": true, "ui": true, "requiredPlugins": ["data"], diff --git a/examples/demo_search/public/async_demo_search_strategy.ts b/examples/demo_search/public/async_demo_search_strategy.ts new file mode 100644 index 00000000000000..7a3f33ce05a75e --- /dev/null +++ b/examples/demo_search/public/async_demo_search_strategy.ts @@ -0,0 +1,69 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable } from 'rxjs'; +import { + ISearchContext, + TSearchStrategyProvider, + ISearchStrategy, +} from '../../../src/plugins/data/public'; + +import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoResponse } from '../common'; +import { ASYNC_SEARCH_STRATEGY } from '../../../x-pack/plugins/data_enhanced/public'; + +/** + * This demo search strategy provider simply provides a shortcut for calling the DEMO_ASYNC_SEARCH_STRATEGY + * on the server side, without users having to pass it in explicitly, and it takes advantage of the + * already registered ASYNC_SEARCH_STRATEGY that exists on the client. + * + * so instead of callers having to do: + * + * ``` + * search( + * { ...request, serverStrategy: DEMO_ASYNC_SEARCH_STRATEGY }, + * options, + * ASYNC_SEARCH_STRATEGY + * ) as Observable, + *``` + + * They can instead just do + * + * ``` + * search(request, options, DEMO_ASYNC_SEARCH_STRATEGY); + * ``` + * + * and are ensured type safety in regard to the request and response objects. + * + * @param context - context supplied by other plugins. + * @param search - a search function to access other strategies that have already been registered. + */ +export const asyncDemoClientSearchStrategyProvider: TSearchStrategyProvider = ( + context: ISearchContext +): ISearchStrategy => { + const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY); + const { search } = asyncStrategyProvider(context); + return { + search: (request, options) => { + return search( + { ...request, serverStrategy: ASYNC_DEMO_SEARCH_STRATEGY }, + options + ) as Observable; + }, + }; +}; diff --git a/examples/demo_search/public/demo_search_strategy.ts b/examples/demo_search/public/demo_search_strategy.ts index cb2480c8a5f194..8dc2779a8544c2 100644 --- a/examples/demo_search/public/demo_search_strategy.ts +++ b/examples/demo_search/public/demo_search_strategy.ts @@ -43,7 +43,7 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; * ``` * context.search(request, options, DEMO_SEARCH_STRATEGY); * ``` - * + * * and are ensured type safety in regard to the request and response objects. * * @param context - context supplied by other plugins. diff --git a/examples/demo_search/public/plugin.ts b/examples/demo_search/public/plugin.ts index 62c912716e627c..a2539cc7a21c5f 100644 --- a/examples/demo_search/public/plugin.ts +++ b/examples/demo_search/public/plugin.ts @@ -19,9 +19,16 @@ import { DataPublicPluginSetup } from '../../../src/plugins/data/public'; import { Plugin, CoreSetup } from '../../../src/core/public'; -import { DEMO_SEARCH_STRATEGY } from '../common'; +import { + DEMO_SEARCH_STRATEGY, + IDemoRequest, + IDemoResponse, + ASYNC_DEMO_SEARCH_STRATEGY, + IAsyncDemoRequest, + IAsyncDemoResponse, +} from '../common'; import { demoClientSearchStrategyProvider } from './demo_search_strategy'; -import { IDemoRequest, IDemoResponse } from '../common'; +import { asyncDemoClientSearchStrategyProvider } from './async_demo_search_strategy'; interface DemoDataSearchSetupDependencies { data: DataPublicPluginSetup; @@ -39,10 +46,12 @@ interface DemoDataSearchSetupDependencies { declare module '../../../src/plugins/data/public' { export interface IRequestTypesMap { [DEMO_SEARCH_STRATEGY]: IDemoRequest; + [ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoRequest; } export interface IResponseTypesMap { [DEMO_SEARCH_STRATEGY]: IDemoResponse; + [ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoResponse; } } @@ -52,6 +61,10 @@ export class DemoDataPlugin implements Plugin { DEMO_SEARCH_STRATEGY, demoClientSearchStrategyProvider ); + deps.data.search.registerSearchStrategyProvider( + ASYNC_DEMO_SEARCH_STRATEGY, + asyncDemoClientSearchStrategyProvider + ); } public start() {} diff --git a/examples/demo_search/server/async_demo_search_strategy.ts b/examples/demo_search/server/async_demo_search_strategy.ts new file mode 100644 index 00000000000000..d2d40891a5d939 --- /dev/null +++ b/examples/demo_search/server/async_demo_search_strategy.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TSearchStrategyProvider } from '../../../src/plugins/data/server'; +import { ASYNC_DEMO_SEARCH_STRATEGY } from '../common'; + +function getFibonacciSequence(n = 0) { + const beginning = [0, 1].slice(0, n); + return Array(Math.max(0, n)) + .fill(null) + .reduce((sequence, value, i) => { + if (i < 2) return sequence; + return [...sequence, sequence[i - 1] + sequence[i - 2]]; + }, beginning); +} + +const generateId = (() => { + let id = 0; + return () => `${id++}`; +})(); + +const loadedMap = new Map(); +const totalMap = new Map(); + +export const asyncDemoSearchStrategyProvider: TSearchStrategyProvider = () => { + return { + search: async request => { + const id = request.id ?? generateId(); + + const loaded = (loadedMap.get(id) ?? 0) + 1; + loadedMap.set(id, loaded); + + const total = request.fibonacciNumbers ?? totalMap.get(id); + totalMap.set(id, total); + + const fibonacciSequence = getFibonacciSequence(loaded); + return { id, total, loaded, fibonacciSequence }; + }, + cancel: async id => { + loadedMap.delete(id); + totalMap.delete(id); + }, + }; +}; diff --git a/examples/demo_search/server/plugin.ts b/examples/demo_search/server/plugin.ts index 653aa217717fa2..49fbae43e3aa28 100644 --- a/examples/demo_search/server/plugin.ts +++ b/examples/demo_search/server/plugin.ts @@ -20,7 +20,15 @@ import { Plugin, CoreSetup, PluginInitializerContext } from 'kibana/server'; import { PluginSetup as DataPluginSetup } from 'src/plugins/data/server'; import { demoSearchStrategyProvider } from './demo_search_strategy'; -import { DEMO_SEARCH_STRATEGY, IDemoRequest, IDemoResponse } from '../common'; +import { + DEMO_SEARCH_STRATEGY, + IDemoRequest, + IDemoResponse, + ASYNC_DEMO_SEARCH_STRATEGY, + IAsyncDemoRequest, + IAsyncDemoResponse, +} from '../common'; +import { asyncDemoSearchStrategyProvider } from './async_demo_search_strategy'; interface IDemoSearchExplorerDeps { data: DataPluginSetup; @@ -38,10 +46,12 @@ interface IDemoSearchExplorerDeps { declare module '../../../src/plugins/data/server' { export interface IRequestTypesMap { [DEMO_SEARCH_STRATEGY]: IDemoRequest; + [ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoRequest; } export interface IResponseTypesMap { [DEMO_SEARCH_STRATEGY]: IDemoResponse; + [ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoResponse; } } @@ -54,6 +64,11 @@ export class DemoDataPlugin implements Plugin id: 'demoSearch', component: , }, + { + title: 'Async demo search strategy', + id: 'asyncDemoSearch', + component: , + }, ]; const routes = pages.map((page, i) => ( diff --git a/examples/search_explorer/public/async_demo_strategy.tsx b/examples/search_explorer/public/async_demo_strategy.tsx new file mode 100644 index 00000000000000..40ddcc1f48fe7a --- /dev/null +++ b/examples/search_explorer/public/async_demo_strategy.tsx @@ -0,0 +1,123 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { + EuiPageContentBody, + EuiFormRow, + EuiFlexItem, + EuiFlexGroup, + EuiFieldNumber, +} from '@elastic/eui'; +import { ISearchGeneric } from '../../../src/plugins/data/public'; +import { DoSearch } from './do_search'; +import { GuideSection } from './guide_section'; + +import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoRequest } from '../../demo_search/common'; + +// @ts-ignore +import demoStrategyServerProvider from '!!raw-loader!./../../demo_search/server/async_demo_search_strategy'; +// @ts-ignore +import demoStrategyPublicProvider from '!!raw-loader!./../../demo_search/public/async_demo_search_strategy'; +// @ts-ignore +import demoStrategyServerPlugin from '!!raw-loader!./../../demo_search/server/plugin'; +// @ts-ignore +import demoStrategyPublicPlugin from '!!raw-loader!./../../demo_search/public/plugin'; + +interface Props { + search: ISearchGeneric; +} + +interface State { + searching: boolean; + fibonacciNumbers: number; + changes: boolean; + error?: any; +} + +export class AsyncDemoStrategy extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { + searching: false, + changes: false, + fibonacciNumbers: 5, + }; + } + + renderDemo = () => { + const request: IAsyncDemoRequest = { + fibonacciNumbers: this.state.fibonacciNumbers, + }; + return ( + + + + + this.setState({ fibonacciNumbers: parseFloat(e.target.value) })} + /> + + + + + this.props.search(request, { signal }, ASYNC_DEMO_SEARCH_STRATEGY) + } + /> + + ); + }; + + render() { + return ( + + + + ); + } +} diff --git a/examples/search_explorer/public/do_search.tsx b/examples/search_explorer/public/do_search.tsx index f279b9fcd6e239..a6b6b9b57db4a9 100644 --- a/examples/search_explorer/public/do_search.tsx +++ b/examples/search_explorer/public/do_search.tsx @@ -118,8 +118,8 @@ ${requestStr} Response: void; + DefaultVisualizationEditor: typeof DefaultEditorController; } let services: VisualizeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts index 8b1bb0fda8c841..d9565938c838d8 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts @@ -43,8 +43,7 @@ export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; export { wrapInI18nContext } from 'ui/i18n'; export { DashboardConstants } from '../dashboard/np_ready/dashboard_constants'; -export { VisSavedObject } from '../../../visualizations/public/embeddable/visualize_embeddable'; -export { VISUALIZE_EMBEDDABLE_TYPE } from '../../../visualizations/public/embeddable'; +export { VisSavedObject, VISUALIZE_EMBEDDABLE_TYPE } from '../../../visualizations/public/'; export { configureAppAngularModule, ensureDefaultIndexPattern, diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js index c40a10115ae4eb..65c25b8cf705d2 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js @@ -30,7 +30,7 @@ export function initVisEditorDirective(app, deps) { appState: '=', }, link: function($scope, element) { - const Editor = $scope.savedObj.vis.type.editor; + const Editor = $scope.savedObj.vis.type.editor || deps.DefaultVisualizationEditor; const editor = new Editor(element[0], $scope.savedObj); $scope.renderFunction = () => { diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index 22804685db3ccd..9f2283d29c203c 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -50,6 +50,7 @@ import { HomePublicPluginSetup, } from '../../../../../plugins/home/public'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; +import { DefaultEditorController } from '../../../vis_default_editor/public'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; @@ -139,7 +140,7 @@ export class VisualizePlugin implements Plugin { localStorage: new Storage(localStorage), navigation, savedObjectsClient, - savedVisualizations: visualizations.getSavedVisualizationsLoader(), + savedVisualizations: visualizations.savedVisualizationsLoader, savedQueryService: dataStart.query.savedQueries, share, toastNotifications: coreStart.notifications.toasts, @@ -150,6 +151,7 @@ export class VisualizePlugin implements Plugin { usageCollection, I18nContext: coreStart.i18n.Context, setActiveUrl, + DefaultVisualizationEditor: DefaultEditorController, }; setServices(deps); diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx index 32ea71c0bc0052..7eee54006f6844 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx @@ -20,8 +20,10 @@ import React, { useEffect, useRef, useState, useCallback } from 'react'; import { EditorRenderProps } from '../../kibana/public/visualize/np_ready/types'; -import { VisualizeEmbeddable } from '../../visualizations/public/embeddable'; -import { VisualizeEmbeddableFactory } from '../../visualizations/public/embeddable/visualize_embeddable_factory'; +import { + VisualizeEmbeddableContract as VisualizeEmbeddable, + VisualizeEmbeddableFactoryContract as VisualizeEmbeddableFactory, +} from '../../visualizations/public/'; import { PanelsContainer, Panel } from '../../../../plugins/kibana_react/public'; import './vis_type_agg_filter'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx b/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx index d3090d277aef9b..db910604eddd15 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import { EditorRenderProps } from 'src/legacy/core_plugins/kibana/public/visualize/np_ready/types'; -import { VisSavedObject } from 'src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable'; +import { VisSavedObject } from 'src/legacy/core_plugins/visualizations/public/'; import { Storage } from '../../../../plugins/kibana_utils/public'; import { KibanaContextProvider } from '../../../../plugins/kibana_react/public'; import { DefaultEditor } from './default_editor'; diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/query_geohash_bounds.ts b/src/legacy/core_plugins/visualizations/public/embeddable/query_geohash_bounds.ts deleted file mode 100644 index f37bc858efab09..00000000000000 --- a/src/legacy/core_plugins/visualizations/public/embeddable/query_geohash_bounds.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@kbn/i18n'; -import { get } from 'lodash'; -import { toastNotifications } from 'ui/notify'; - -import { IAggConfig } from 'ui/agg_types'; -import { timefilter } from 'ui/timefilter'; -import { Vis } from '../np_ready/public'; -import { Filter, Query, SearchSource, ISearchSource } from '../../../../../plugins/data/public'; - -interface QueryGeohashBoundsParams { - filters?: Filter[]; - query?: Query; - searchSource?: ISearchSource; -} - -/** - * Coordinate map visualization needs to be able to query for the latest geohash - * bounds when a user clicks the "fit to data" map icon, which requires knowing - * about global filters & queries. This logic has been extracted here so we can - * keep `searchSource` out of the vis, but ultimately we need to design a - * long-term solution for situations like this. - * - * TODO: Remove this as a part of elastic/kibana#30593 - */ -export async function queryGeohashBounds(vis: Vis, params: QueryGeohashBoundsParams) { - const agg = vis.getAggConfig().aggs.find((a: IAggConfig) => { - return get(a, 'type.dslName') === 'geohash_grid'; - }); - - if (agg) { - const searchSource = params.searchSource - ? params.searchSource.createChild() - : new SearchSource(); - searchSource.setField('size', 0); - searchSource.setField('aggs', () => { - const geoBoundsAgg = vis.getAggConfig().createAggConfig( - { - type: 'geo_bounds', - enabled: true, - params: { - field: agg.getField(), - }, - schema: 'metric', - }, - { - addToAggConfigs: false, - } - ); - return { - '1': geoBoundsAgg.toDsl(), - }; - }); - - const { filters, query } = params; - if (filters) { - searchSource.setField('filter', () => { - const activeFilters = [...filters]; - const indexPattern = agg.getIndexPattern(); - const useTimeFilter = !!indexPattern.timeFieldName; - if (useTimeFilter) { - const filter = timefilter.createFilter(indexPattern); - if (filter) activeFilters.push((filter as any) as Filter); - } - return activeFilters; - }); - } - if (query) { - searchSource.setField('query', query); - } - - try { - const esResp = await searchSource.fetch(); - return get(esResp, 'aggregations.1.bounds'); - } catch (error) { - toastNotifications.addDanger({ - title: i18n.translate('visualizations.queryGeohashBounds.unableToGetBoundErrorTitle', { - defaultMessage: 'Unable to get bounds', - }), - text: `${error.message}`, - }); - return; - } - } -} diff --git a/src/legacy/core_plugins/visualizations/public/index.scss b/src/legacy/core_plugins/visualizations/public/index.scss index 748945eabd3316..238f58fbfa2955 100644 --- a/src/legacy/core_plugins/visualizations/public/index.scss +++ b/src/legacy/core_plugins/visualizations/public/index.scss @@ -1,4 +1,2 @@ @import 'src/legacy/ui/public/styles/styling_constants'; - -@import './embeddable/index'; @import './np_ready/public/index'; diff --git a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts index 5cff588d951b04..223c130df3505f 100644 --- a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts +++ b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts @@ -24,9 +24,5 @@ export { IAggConfigs, isDateHistogramBucketAggConfig, setBounds, -} from '../../../ui/public/agg_types'; -export { createFormat } from '../../../ui/public/visualize/loader/pipeline_helpers/utilities'; -export { I18nContext } from '../../../ui/public/i18n'; -import chrome from '../../../ui/public/chrome'; -export { chrome as legacyChrome }; -import '../../../ui/public/directives/bind'; +} from '../../data/public'; +export { createSavedSearchesLoader } from '../../kibana/public/discover/saved_searches/'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json b/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json index 888edde44a2611..d4f9bd327d6aca 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json +++ b/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json @@ -3,8 +3,5 @@ "version": "kibana", "server": false, "ui": true, - "requiredPlugins": [ - "data", - "search" - ] + "requiredPlugins": ["data", "search", "expressions", "uiActions"] } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/_index.scss b/src/legacy/core_plugins/visualizations/public/np_ready/public/_index.scss index d87b6b004a5113..eada763b63c4d4 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/_index.scss +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/_index.scss @@ -1 +1,2 @@ @import 'wizard/index'; +@import 'embeddable/index'; diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/_embeddables.scss b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_embeddables.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/embeddable/_embeddables.scss rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_embeddables.scss diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/_index.scss b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_index.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/embeddable/_index.scss rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_index.scss diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/_visualize_lab_disabled.scss b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_visualize_lab_disabled.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/embeddable/_visualize_lab_disabled.scss rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_visualize_lab_disabled.scss diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/constants.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/constants.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/embeddable/constants.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/constants.ts diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/disabled_lab_embeddable.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_embeddable.tsx similarity index 94% rename from src/legacy/core_plugins/visualizations/public/embeddable/disabled_lab_embeddable.tsx rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_embeddable.tsx index f9dfd5d2b98f41..fbb2eba3afe79a 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/disabled_lab_embeddable.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_embeddable.tsx @@ -19,7 +19,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Embeddable, EmbeddableOutput } from '../../../../../plugins/embeddable/public'; +import { Embeddable, EmbeddableOutput } from '../../../../../../../plugins/embeddable/public'; import { DisabledLabVisualization } from './disabled_lab_visualization'; import { VisualizeInput } from './visualize_embeddable'; diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/disabled_lab_visualization.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_visualization.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/embeddable/disabled_lab_visualization.tsx rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_visualization.tsx diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/get_index_pattern.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts similarity index 90% rename from src/legacy/core_plugins/visualizations/public/embeddable/get_index_pattern.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts index cfb2960cfbb7cd..51d839275fd27d 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/get_index_pattern.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts @@ -17,13 +17,13 @@ * under the License. */ -import { VisSavedObject } from './visualize_embeddable'; +import { VisSavedObject } from '../types'; import { indexPatterns, IIndexPattern, IndexPatternAttributes, -} from '../../../../../plugins/data/public'; -import { getUISettings, getSavedObjects } from '../np_ready/public/services'; +} from '../../../../../../../plugins/data/public'; +import { getUISettings, getSavedObjects } from '../services'; export async function getIndexPattern( savedVis: VisSavedObject diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/index.ts similarity index 92% rename from src/legacy/core_plugins/visualizations/public/embeddable/index.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/index.ts index d7c0205891ec57..a1cd31eebef205 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/index.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/index.ts @@ -18,4 +18,5 @@ */ export { DisabledLabEmbeddable } from './disabled_lab_embeddable'; export { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable'; +export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory'; export { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts similarity index 89% rename from src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts index 72a0ef72b56937..2537caa01cd46d 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts @@ -18,13 +18,8 @@ */ import _, { get } from 'lodash'; -import { PersistedState } from 'ui/persisted_state'; import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; -import { buildPipeline } from 'ui/visualize/loader/pipeline_helpers'; -import { npStart } from 'ui/new_platform'; -import { IExpressionLoaderParams } from 'src/plugins/expressions/public'; -import { EmbeddableVisTriggerContext } from 'src/plugins/embeddable/public'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; import { IIndexPattern, @@ -32,9 +27,8 @@ import { Query, esFilters, Filter, - ISearchSource, TimefilterContract, -} from '../../../../../plugins/data/public'; +} from '../../../../../../../plugins/data/public'; import { EmbeddableInput, EmbeddableOutput, @@ -42,27 +36,21 @@ import { Container, selectRangeTrigger, valueClickTrigger, -} from '../../../../../plugins/embeddable/public'; -import { dispatchRenderComplete } from '../../../../../plugins/kibana_utils/public'; -import { SavedObject } from '../../../../../plugins/saved_objects/public'; -import { SavedSearch } from '../../../kibana/public/discover/np_ready/types'; -import { Vis } from '../np_ready/public'; + EmbeddableVisTriggerContext, +} from '../../../../../../../plugins/embeddable/public'; +import { dispatchRenderComplete } from '../../../../../../../plugins/kibana_utils/public'; +import { + IExpressionLoaderParams, + ExpressionsStart, +} from '../../../../../../../plugins/expressions/public'; +import { buildPipeline } from '../legacy/build_pipeline'; +import { Vis } from '../vis'; +import { getExpressions, getUiActions } from '../services'; +import { PersistedState } from '../../../legacy_imports'; +import { VisSavedObject } from '../types'; const getKeys = (o: T): Array => Object.keys(o) as Array; -export interface VisSavedObject extends SavedObject { - vis: Vis; - description?: string; - searchSource: ISearchSource; - title: string; - uiStateJSON?: string; - destroy: () => void; - savedSearchRefName?: string; - savedSearchId?: string; - savedSearch?: SavedSearch; - visState: any; -} - export interface VisualizeEmbeddableConfiguration { savedVisualization: VisSavedObject; indexPatterns?: IIndexPattern[]; @@ -90,7 +78,7 @@ export interface VisualizeOutput extends EmbeddableOutput { visTypeName: string; } -type ExpressionLoader = InstanceType; +type ExpressionLoader = InstanceType; export class VisualizeEmbeddable extends Embeddable { private handler?: ExpressionLoader; @@ -281,7 +269,8 @@ export class VisualizeEmbeddable extends Embeddable { @@ -309,7 +298,9 @@ export class VisualizeEmbeddable extends Embeddable { public readonly type = VISUALIZE_EMBEDDABLE_TYPE; - constructor( - private timefilter: TimefilterContract, - private getSavedVisualizationsLoader: () => SavedVisualizations - ) { + constructor() { super({ savedObjectMetaData: { name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }), @@ -101,7 +98,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< input: Partial & { id: string }, parent?: Container ): Promise { - const savedVisualizations = this.getSavedVisualizationsLoader(); + const savedVisualizations = getSavedVisualizationsLoader(); try { const visId = savedObject.id as string; @@ -118,7 +115,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< const indexPattern = await getIndexPattern(savedObject); const indexPatterns = indexPattern ? [indexPattern] : []; return new VisualizeEmbeddable( - this.timefilter, + getTimeFilter(), { savedVisualization: savedObject, indexPatterns, @@ -141,7 +138,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< input: Partial & { id: string }, parent?: Container ): Promise { - const savedVisualizations = this.getSavedVisualizationsLoader(); + const savedVisualizations = getSavedVisualizationsLoader(); try { const savedObject = await savedVisualizations.get(savedObjectId); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts index f1c1677a60f260..4ac0931c5d8655 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts @@ -19,8 +19,11 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { VisResponseValue } from 'src/plugins/visualizations/public'; -import { ExpressionFunctionDefinition, Render } from 'src/plugins/expressions/public'; +import { VisResponseValue } from '../../../../../../../plugins/visualizations/public'; +import { + ExpressionFunctionDefinition, + Render, +} from '../../../../../../../plugins/expressions/public'; import { PersistedState } from '../../../legacy_imports'; import { getTypes, getIndexPatterns, getFilterManager } from '../services'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts index 3c4a1c1449d475..34ffb698e5f8c7 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts @@ -29,22 +29,28 @@ * either types, or static code. */ -import { PluginInitializerContext } from 'src/core/public'; +import { PublicContract } from '@kbn/utility-types'; +import { PluginInitializerContext } from '../../../../../../core/public'; import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin'; /** @public */ export { VisualizationsSetup, VisualizationsStart }; /** @public types */ -export { VisTypeAlias, VisType } from './types'; +export { VisTypeAlias, VisType } from './vis_types'; +export { VisSavedObject } from './types'; +export { Vis, VisParams, VisState } from './vis'; +import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable'; +export type VisualizeEmbeddableFactoryContract = PublicContract; +export type VisualizeEmbeddableContract = PublicContract; export function plugin(initializerContext: PluginInitializerContext) { return new VisualizationsPlugin(initializerContext); } /** @public static code */ -export { Vis, VisParams, VisState } from './vis'; -export { TypesService } from './types/types_service'; +export { TypesService } from './vis_types/types_service'; +export { VISUALIZE_EMBEDDABLE_TYPE, VisualizeInput } from './embeddable'; export { Status } from './legacy/update_status'; export { buildPipeline, buildVislibDimensions, SchemaConfig } from './legacy/build_pipeline'; @@ -52,4 +58,4 @@ export { buildPipeline, buildVislibDimensions, SchemaConfig } from './legacy/bui // @ts-ignore export { updateOldState } from './legacy/vis_update_state'; export { calculateObjectHash } from './legacy/calculate_object_hash'; -export { createSavedVisLoader } from '../../saved_visualizations/saved_visualizations'; +export { createSavedVisLoader } from './saved_visualizations/saved_visualizations'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts index 41b23b276e88d7..57c686b6e9cb04 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts @@ -17,12 +17,12 @@ * under the License. */ -import { PluginInitializerContext } from 'src/core/public'; - /* eslint-disable @kbn/eslint/no-restricted-paths */ import { npSetup, npStart } from 'ui/new_platform'; /* eslint-enable @kbn/eslint/no-restricted-paths */ +import { PluginInitializerContext } from '../../../../../../core/public'; + import { plugin } from '.'; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/base_vis_type.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/base_vis_type.js index b8aa33d0a5abe8..9c1dfd9780255b 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/base_vis_type.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/base_vis_type.js @@ -19,7 +19,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { BaseVisType } from '../../../types/base_vis_type'; +import { BaseVisType } from '../../../vis_types/base_vis_type'; describe('Base Vis Type', function() { beforeEach(ngMock.module('kibana')); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/react_vis_type.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/react_vis_type.js index c85557ea1b0b09..2474a588704245 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/react_vis_type.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/react_vis_type.js @@ -19,7 +19,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { ReactVisType } from '../../../types/react_vis_type'; +import { ReactVisType } from '../../../vis_types/react_vis_type'; describe('React Vis Type', function() { const visConfig = { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts index f73dc3e19d0ef3..1adf6fd23f5a50 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts @@ -31,11 +31,6 @@ import { IAggConfig } from '../../../legacy_imports'; import { searchSourceMock } from '../../../legacy_mocks'; jest.mock('ui/new_platform'); -jest.mock('ui/agg_types', () => ({ - setBounds: () => {}, - dateHistogramBucketAgg: () => {}, - isDateHistogramBucketAggConfig: () => true, -})); describe('visualize loader pipeline helpers: build pipeline', () => { describe('prepareJson', () => { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts index 025eef834ca86b..155213b4103b03 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts @@ -18,17 +18,11 @@ */ import { cloneDeep, get } from 'lodash'; -// @ts-ignore import moment from 'moment'; -import { SerializedFieldFormat } from 'src/plugins/expressions/public'; -import { ISearchSource } from 'src/plugins/data/public'; -import { - IAggConfig, - setBounds, - isDateHistogramBucketAggConfig, - createFormat, -} from '../../../legacy_imports'; -import { Vis, VisParams } from '..'; +import { SerializedFieldFormat } from '../../../../../../../plugins/expressions/public'; +import { fieldFormats, ISearchSource } from '../../../../../../../plugins/data/public'; +import { IAggConfig, setBounds, isDateHistogramBucketAggConfig } from '../../../legacy_imports'; +import { Vis, VisParams } from '../types'; interface SchemaConfigParams { precision?: number; @@ -102,7 +96,7 @@ export const getSchemas = (vis: Vis, timeRange?: any): Schemas => { 'max_bucket', ].includes(agg.type.name); - const format = createFormat( + const format = fieldFormats.serialize( hasSubAgg ? agg.params.customMetric || agg.aggConfigs.getRequestAggById(agg.params.metricAgg) : agg diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts index 9fb87cadb29838..b3dd22f62f81f1 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts @@ -23,7 +23,7 @@ jest.mock('ui/vis/vis_factory'); jest.mock('ui/registry/vis_types'); jest.mock('./types/vis_type_alias_registry'); -import { PluginInitializerContext } from 'src/core/public'; +import { PluginInitializerContext } from '../../../../../../core/public'; import { VisualizationsSetup, VisualizationsStart } from './'; import { VisualizationsPlugin } from './plugin'; import { coreMock } from '../../../../../../core/public/mocks'; @@ -31,6 +31,7 @@ import { embeddablePluginMock } from '../../../../../../plugins/embeddable/publi import { expressionsPluginMock } from '../../../../../../plugins/expressions/public/mocks'; import { dataPluginMock } from '../../../../../../plugins/data/public/mocks'; import { usageCollectionPluginMock } from '../../../../../../plugins/usage_collection/public/mocks'; +import { uiActionsPluginMock } from '../../../../../../plugins/ui_actions/public/mocks'; const createSetupContract = (): VisualizationsSetup => ({ types: { @@ -47,7 +48,7 @@ const createStartContract = (): VisualizationsStart => ({ all: jest.fn(), getAliases: jest.fn(), }, - getSavedVisualizationsLoader: jest.fn(), + savedVisualizationsLoader: {} as any, showNewVisModal: jest.fn(), Vis: jest.fn(), }); @@ -64,6 +65,8 @@ const createInstance = async () => { const doStart = () => plugin.start(coreMock.createStart(), { data: dataPluginMock.createStartContract(), + expressions: expressionsPluginMock.createStartContract(), + uiActions: uiActionsPluginMock.createStartContract(), }); return { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts index 20bed59faad88b..e1d87d414d398d 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts @@ -17,8 +17,13 @@ * under the License. */ -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { TypesService, TypesSetup, TypesStart } from './types'; +import { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, +} from '../../../../../../core/public'; +import { TypesService, TypesSetup, TypesStart } from './vis_types'; import { setUISettings, setTypes, @@ -29,10 +34,13 @@ import { setSavedObjects, setUsageCollector, setFilterManager, + setExpressions, + setUiActions, + setSavedVisualizationsLoader, + setTimeFilter, } from './services'; -import { VisualizeEmbeddableFactory } from '../../embeddable/visualize_embeddable_factory'; -import { VISUALIZE_EMBEDDABLE_TYPE } from '../../embeddable'; -import { ExpressionsSetup } from '../../../../../../plugins/expressions/public'; +import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeEmbeddableFactory } from './embeddable'; +import { ExpressionsSetup, ExpressionsStart } from '../../../../../../plugins/expressions/public'; import { IEmbeddableSetup } from '../../../../../../plugins/embeddable/public'; import { visualization as visualizationFunction } from './expressions/visualization_function'; import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; @@ -41,13 +49,11 @@ import { DataPublicPluginStart, } from '../../../../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; -import { - createSavedVisLoader, - SavedObjectKibanaServicesWithVisualizations, -} from '../../saved_visualizations'; -import { SavedVisualizations } from '../../../../kibana/public/visualize/np_ready/types'; +import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations'; import { VisImpl, VisImplConstructor } from './vis_impl'; import { showNewVisModal } from './wizard'; +import { UiActionsStart } from '../../../../../../plugins/ui_actions/public'; + /** * Interface for this plugin's returned setup/start contracts. * @@ -59,9 +65,9 @@ export interface VisualizationsSetup { export interface VisualizationsStart { types: TypesStart; - getSavedVisualizationsLoader: () => SavedVisualizations; - showNewVisModal: typeof showNewVisModal; + savedVisualizationsLoader: SavedVisualizationsLoader; Vis: VisImplConstructor; + showNewVisModal: typeof showNewVisModal; } export interface VisualizationsSetupDeps { @@ -73,6 +79,8 @@ export interface VisualizationsSetupDeps { export interface VisualizationsStartDeps { data: DataPublicPluginStart; + expressions: ExpressionsStart; + uiActions: UiActionsStart; } /** @@ -92,8 +100,6 @@ export class VisualizationsPlugin VisualizationsStartDeps > { private readonly types: TypesService = new TypesService(); - private savedVisualizations?: SavedVisualizations; - private savedVisualizationDependencies?: SavedObjectKibanaServicesWithVisualizations; constructor(initializerContext: PluginInitializerContext) {} @@ -107,10 +113,7 @@ export class VisualizationsPlugin expressions.registerFunction(visualizationFunction); expressions.registerRenderer(visualizationRenderer); - const embeddableFactory = new VisualizeEmbeddableFactory( - data.query.timefilter.timefilter, - this.getSavedVisualizationsLoader - ); + const embeddableFactory = new VisualizeEmbeddableFactory(); embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); return { @@ -118,7 +121,10 @@ export class VisualizationsPlugin }; } - public start(core: CoreStart, { data }: VisualizationsStartDeps): VisualizationsStart { + public start( + core: CoreStart, + { data, expressions, uiActions }: VisualizationsStartDeps + ): VisualizationsStart { const types = this.types.start(); setI18n(core.i18n); setTypes(types); @@ -127,31 +133,27 @@ export class VisualizationsPlugin setSavedObjects(core.savedObjects); setIndexPatterns(data.indexPatterns); setFilterManager(data.query.filterManager); - - this.savedVisualizationDependencies = { + setExpressions(expressions); + setUiActions(uiActions); + setTimeFilter(data.query.timefilter.timefilter); + const savedVisualizationsLoader = createSavedVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, chrome: core.chrome, overlays: core.overlays, visualizationTypes: types, - }; + }); + setSavedVisualizationsLoader(savedVisualizationsLoader); return { types, - getSavedVisualizationsLoader: () => this.getSavedVisualizationsLoader(), showNewVisModal, Vis: VisImpl, + savedVisualizationsLoader, }; } public stop() { this.types.stop(); } - - private getSavedVisualizationsLoader = () => { - if (!this.savedVisualizations) { - this.savedVisualizations = createSavedVisLoader(this.savedVisualizationDependencies!); - } - return this.savedVisualizations; - }; } diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts similarity index 90% rename from src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts index f4548da3752165..f3539b3564c563 100644 --- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts @@ -28,15 +28,13 @@ import { createSavedObjectClass, SavedObject, SavedObjectKibanaServices, -} from '../../../../../plugins/saved_objects/public'; -import { updateOldState } from '../index'; +} from '../../../../../../../plugins/saved_objects/public'; +import { updateOldState } from '../../../index'; import { extractReferences, injectReferences } from './saved_visualization_references'; -import { IIndexPattern } from '../../../../../plugins/data/public'; -import { VisSavedObject } from '../embeddable/visualize_embeddable'; - -import { createSavedSearchesLoader } from '../../../kibana/public/discover'; -import { VisualizeConstants } from '../../../kibana/public/visualize'; -import { VisImpl } from '../np_ready/public/vis_impl'; +import { IIndexPattern } from '../../../../../../../plugins/data/public'; +import { VisSavedObject } from '../types'; +import { VisImpl } from '../vis_impl'; +import { createSavedSearchesLoader } from '../../../legacy_imports'; async function _afterEsResp(savedVis: VisSavedObject, services: any) { await _getLinkedSavedSearch(savedVis, services); @@ -138,7 +136,7 @@ export function createSavedVisClass(services: SavedObjectKibanaServices) { }); this.showInRecentlyAccessed = true; this.getFullPath = () => { - return `/app/kibana#${VisualizeConstants.EDIT_PATH}/${this.id}`; + return `/app/kibana#/visualize/edit/${this.id}`; }; } } diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/find_list_items.test.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.test.ts similarity index 77% rename from src/legacy/core_plugins/visualizations/public/saved_visualizations/find_list_items.test.js rename to src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.test.ts index ed0f6dc429ef45..d1def09978dbb7 100644 --- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/find_list_items.test.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.test.ts @@ -18,19 +18,24 @@ */ import { findListItems } from './find_list_items'; +import { coreMock } from '../../../../../../../core/public/mocks'; +import { SavedObjectsClientContract } from '../../../../../../../core/public'; +import { VisTypeAlias } from '../vis_types'; describe('saved_visualizations', () => { function testProps() { + const savedObjects = coreMock.createStart().savedObjects.client as jest.Mocked< + SavedObjectsClientContract + >; + (savedObjects.find as jest.Mock).mockImplementation(() => ({ + total: 0, + savedObjects: [], + })); return { visTypes: [], search: '', size: 10, - savedObjectsClient: { - find: jest.fn(async () => ({ - total: 0, - savedObjects: [], - })), - }, + savedObjectsClient: savedObjects, mapSavedObjectApiHits: jest.fn(), }; } @@ -60,7 +65,7 @@ describe('saved_visualizations', () => { searchFields: ['baz', 'bing'], }, }, - }, + } as VisTypeAlias, ], }; const { find } = props.savedObjectsClient; @@ -86,7 +91,7 @@ describe('saved_visualizations', () => { searchFields: ['baz', 'bing', 'barfield'], }, }, - }, + } as VisTypeAlias, { appExtensions: { visualizations: { @@ -94,7 +99,7 @@ describe('saved_visualizations', () => { searchFields: ['baz', 'bing', 'foofield'], }, }, - }, + } as VisTypeAlias, ], }; const { find } = props.savedObjectsClient; @@ -128,24 +133,11 @@ describe('saved_visualizations', () => { it('uses type-specific toListItem function, if available', async () => { const props = { ...testProps(), - savedObjectsClient: { - find: jest.fn(async () => ({ - total: 2, - savedObjects: [ - { - id: 'lotr', - type: 'wizard', - attributes: { label: 'Gandalf' }, - }, - { - id: 'wat', - type: 'visualization', - attributes: { title: 'WATEVER' }, - }, - ], - })), - }, - mapSavedObjectApiHits(savedObject) { + mapSavedObjectApiHits(savedObject: { + id: string; + type: string; + attributes: { title: string }; + }) { return { id: savedObject.id, title: `DEFAULT ${savedObject.attributes.title}`, @@ -159,14 +151,31 @@ describe('saved_visualizations', () => { toListItem(savedObject) { return { id: savedObject.id, - title: `${savedObject.attributes.label} THE GRAY`, + title: `${(savedObject.attributes as { label: string }).label} THE GRAY`, }; }, }, }, - }, + } as VisTypeAlias, ], }; + + (props.savedObjectsClient.find as jest.Mock).mockImplementationOnce(async () => ({ + total: 2, + savedObjects: [ + { + id: 'lotr', + type: 'wizard', + attributes: { label: 'Gandalf' }, + }, + { + id: 'wat', + type: 'visualization', + attributes: { title: 'WATEVER' }, + }, + ], + })); + const items = await findListItems(props); expect(items).toEqual({ total: 2, diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/find_list_items.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.ts similarity index 64% rename from src/legacy/core_plugins/visualizations/public/saved_visualizations/find_list_items.js rename to src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.ts index a7fcee67adf722..02db90a762e89f 100644 --- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/find_list_items.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.ts @@ -18,6 +18,13 @@ */ import _ from 'lodash'; +import { + SavedObjectAttributes, + SavedObjectsClientContract, +} from '../../../../../../../core/public'; +import { SavedObjectLoader } from '../../../../../../../plugins/saved_objects/public'; +import { VisTypeAlias } from '../vis_types'; +import { VisualizationsAppExtension } from '../vis_types/vis_type_alias_registry'; /** * Search for visualizations and convert them into a list display-friendly format. @@ -28,34 +35,42 @@ export async function findListItems({ size, savedObjectsClient, mapSavedObjectApiHits, +}: { + search: string; + size: number; + visTypes: VisTypeAlias[]; + savedObjectsClient: SavedObjectsClientContract; + mapSavedObjectApiHits: SavedObjectLoader['mapSavedObjectApiHits']; }) { - const extensions = _.compact( - visTypes.map(v => v.appExtensions && v.appExtensions.visualizations) - ); + const extensions = visTypes + .map(v => v.appExtensions?.visualizations) + .filter(Boolean) as VisualizationsAppExtension[]; const extensionByType = extensions.reduce((acc, m) => { - return m.docTypes.reduce((_acc, type) => { + return m!.docTypes.reduce((_acc, type) => { acc[type] = m; return acc; }, acc); - }, {}); - const searchOption = (field, ...defaults) => + }, {} as { [visType: string]: VisualizationsAppExtension }); + const searchOption = (field: string, ...defaults: string[]) => _(extensions) .pluck(field) .concat(defaults) .compact() .flatten() .uniq() - .value(); + .value() as string[]; const searchOptions = { type: searchOption('docTypes', 'visualization'), searchFields: searchOption('searchFields', 'title^3', 'description'), search: search ? `${search}*` : undefined, perPage: size, page: 1, - defaultSearchOperator: 'AND', + defaultSearchOperator: 'AND' as 'AND', }; - const { total, savedObjects } = await savedObjectsClient.find(searchOptions); + const { total, savedObjects } = await savedObjectsClient.find( + searchOptions + ); return { total, diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/saved_visualizations/index.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts similarity index 96% rename from src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts index 6549b317d16346..98af6d99025c21 100644 --- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts @@ -18,7 +18,7 @@ */ import { extractReferences, injectReferences } from './saved_visualization_references'; -import { VisSavedObject } from '../embeddable/visualize_embeddable'; +import { VisSavedObject, VisState } from '../types'; describe('extractReferences', () => { test('extracts nothing if savedSearchId is empty', () => { @@ -128,7 +128,7 @@ Object { id: '1', title: 'test', savedSearchRefName: 'search_0', - visState: { + visState: ({ params: { controls: [ { @@ -140,7 +140,7 @@ Object { }, ], }, - }, + } as unknown) as VisState, } as VisSavedObject; const references = [ { @@ -192,7 +192,7 @@ Object { const context = { id: '1', title: 'test', - visState: { + visState: ({ params: { controls: [ { @@ -201,7 +201,7 @@ Object { }, ], }, - }, + } as unknown) as VisState, } as VisSavedObject; expect(() => injectReferences(context, [])).toThrowErrorMatchingInlineSnapshot( `"Could not find index pattern reference \\"control_0_index_pattern\\""` diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualization_references.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.ts similarity index 95% rename from src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualization_references.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.ts index 330f5e2dacd10a..b995d340d44d9e 100644 --- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualization_references.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.ts @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { SavedObjectAttributes, SavedObjectReference } from 'kibana/public'; -import { VisSavedObject } from '../embeddable/visualize_embeddable'; +import { SavedObjectAttributes, SavedObjectReference } from '../../../../../../../core/public'; +import { VisSavedObject } from '../types'; export function extractReferences({ attributes, diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualizations.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualizations.ts similarity index 91% rename from src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualizations.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualizations.ts index 7d0d6a10ff66f3..fc0f77d54059c0 100644 --- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualizations.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualizations.ts @@ -19,18 +19,15 @@ import { SavedObjectLoader, SavedObjectKibanaServices, -} from '../../../../../plugins/saved_objects/public'; - -// @ts-ignore +} from '../../../../../../../plugins/saved_objects/public'; import { findListItems } from './find_list_items'; import { createSavedVisClass } from './_saved_vis'; -import { createVisualizeEditUrl } from '../../../kibana/public/visualize'; -import { TypesStart } from '../np_ready/public/types'; +import { TypesStart } from '../vis_types'; export interface SavedObjectKibanaServicesWithVisualizations extends SavedObjectKibanaServices { visualizationTypes: TypesStart; } - +export type SavedVisualizationsLoader = ReturnType; export function createSavedVisLoader(services: SavedObjectKibanaServicesWithVisualizations) { const { savedObjectsClient, visualizationTypes } = services; @@ -59,7 +56,7 @@ export function createSavedVisLoader(services: SavedObjectKibanaServicesWithVisu source.icon = source.type.icon; source.image = source.type.image; source.typeTitle = source.type.title; - source.editUrl = `#${createVisualizeEditUrl(id)}`; + source.editUrl = `#/visualize/edit/${id}`; return source; }; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts index 433c5c7b6df0dc..a977a4b452bf7f 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts @@ -23,11 +23,18 @@ import { I18nStart, IUiSettingsClient, SavedObjectsStart, -} from 'src/core/public'; -import { TypesStart } from './types'; +} from '../../../../../../core/public'; +import { TypesStart } from './vis_types'; import { createGetterSetter } from '../../../../../../plugins/kibana_utils/public'; -import { FilterManager, IndexPatternsContract } from '../../../../../../plugins/data/public'; +import { + FilterManager, + IndexPatternsContract, + TimefilterContract, +} from '../../../../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; +import { ExpressionsStart } from '../../../../../../plugins/expressions/public'; +import { UiActionsStart } from '../../../../../../plugins/ui_actions/public'; +import { SavedVisualizationsLoader } from './saved_visualizations'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -47,6 +54,8 @@ export const [getFilterManager, setFilterManager] = createGetterSetter('TimeFilter'); + export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( 'IndexPatterns' ); @@ -54,3 +63,11 @@ export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( 'UsageCollection' ); + +export const [getExpressions, setExpressions] = createGetterSetter('Expressions'); + +export const [getUiActions, setUiActions] = createGetterSetter('UiActions'); + +export const [getSavedVisualizationsLoader, setSavedVisualizationsLoader] = createGetterSetter< + SavedVisualizationsLoader +>('SavedVisualisationsLoader'); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts new file mode 100644 index 00000000000000..d2ca4ffb92eb2b --- /dev/null +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObject } from '../../../../../../plugins/saved_objects/public'; +import { Vis, VisState, VisParams, VisualizationController } from './vis'; +import { ISearchSource } from '../../../../../../plugins/data/public/'; +import { SavedSearch } from '../../../../kibana/public/discover/np_ready/types'; + +export { Vis, VisState, VisParams, VisualizationController }; + +export interface VisSavedObject extends SavedObject { + vis: Vis; + description?: string; + searchSource: ISearchSource; + title: string; + uiStateJSON?: string; + destroy: () => void; + savedSearchRefName?: string; + savedSearchId?: string; + savedSearch?: SavedSearch; + visState: VisState; +} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts index 19375e25a9fb72..990f27dca7556c 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts @@ -17,7 +17,7 @@ * under the License. */ -import { VisType } from './types'; +import { VisType } from './vis_types'; import { IAggConfigs } from '../../legacy_imports'; import { Status } from './legacy/update_status'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts index f9b7db5c02d937..62b68082e21f87 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts @@ -18,7 +18,7 @@ */ import { Vis, VisState, VisParams } from './vis'; -import { VisType } from './types'; +import { VisType } from './vis_types'; import { IIndexPattern } from '../../../../../../plugins/data/common'; type InitVisStateType = diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/base_vis_type.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/base_vis_type.js similarity index 95% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/types/base_vis_type.js rename to src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/base_vis_type.js index 351acc48e26766..50ff74cfe9dd35 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/base_vis_type.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/base_vis_type.js @@ -19,8 +19,6 @@ import _ from 'lodash'; -import { DefaultEditorController } from '../../../../../vis_default_editor/public'; - export class BaseVisType { constructor(opts = {}) { if (!opts.name) { @@ -47,7 +45,7 @@ export class BaseVisType { }, requestHandler: 'courier', // select one from registry or pass a function responseHandler: 'none', - editor: DefaultEditorController, + editor: null, // no default is provided editorConfig: { collections: {}, // collections used for configuration (list of positions, ...) }, diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/types/index.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/react_vis_type.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/react_vis_type.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/types/react_vis_type.js rename to src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/react_vis_type.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/types_service.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/types_service.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/types/types_service.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/types_service.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/vis_type_alias_registry.ts similarity index 97% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/vis_type_alias_registry.ts index 97f4798c296d42..12b02ee9e6b32a 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/vis_type_alias_registry.ts @@ -27,7 +27,7 @@ interface VisualizationListItem { typeTitle: string; } -interface VisualizationsAppExtension { +export interface VisualizationsAppExtension { docTypes: string[]; searchFields?: string[]; toListItem: (savedObject: { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.test.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.test.tsx index 58f7bc49d1cdb8..2712019e426095 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.test.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.test.tsx @@ -19,9 +19,9 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypesStart, VisType } from '../types'; +import { TypesStart, VisType } from '../vis_types'; import { NewVisModal } from './new_vis_modal'; -import { SavedObjectsStart } from 'kibana/public'; +import { SavedObjectsStart } from '../../../../../../../core/public'; describe('NewVisModal', () => { const { location } = window; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.tsx index b39e8e8926707f..7c10001eddb504 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.tsx @@ -23,10 +23,10 @@ import { EuiModal, EuiOverlayMask } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics'; -import { IUiSettingsClient, SavedObjectsStart } from 'kibana/public'; +import { IUiSettingsClient, SavedObjectsStart } from '../../../../../../../core/public'; import { SearchSelection } from './search_selection'; import { TypeSelection } from './type_selection'; -import { TypesStart, VisType, VisTypeAlias } from '../types'; +import { TypesStart, VisType, VisTypeAlias } from '../vis_types'; import { UsageCollectionSetup } from '../../../../../../../plugins/usage_collection/public'; interface TypeSelectionProps { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/search_selection.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/search_selection.tsx index 9b3b8a6425e523..f8eb191dd5f925 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/search_selection.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/search_selection.tsx @@ -21,10 +21,10 @@ import { EuiModalBody, EuiModalHeader, EuiModalHeaderTitle } from '@elastic/eui' import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { IUiSettingsClient, SavedObjectsStart } from 'kibana/public'; +import { IUiSettingsClient, SavedObjectsStart } from '../../../../../../../../core/public'; import { SavedObjectFinderUi } from '../../../../../../../../plugins/saved_objects/public'; -import { VisType } from '../../types'; +import { VisType } from '../../vis_types'; interface SearchSelectionProps { onSearchSelected: (searchId: string, searchType: string) => void; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.tsx index 5068f43952c4ef..e84314853ba4c7 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.tsx @@ -21,7 +21,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { Fragment } from 'react'; import { EuiText, EuiButton } from '@elastic/eui'; import { VisTypeAliasListEntry } from './type_selection'; -import { VisTypeAlias } from '../../types'; +import { VisTypeAlias } from '../../vis_types'; interface Props { promotedTypes: VisTypeAliasListEntry[]; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/type_selection.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/type_selection.tsx index 574f5b3cccc99a..81dcecfee26131 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/type_selection.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/type_selection.tsx @@ -40,7 +40,7 @@ import { VisTypeAlias } from '../../../../../../visualizations/public'; import { NewVisHelp } from './new_vis_help'; import { VisHelpText } from './vis_help_text'; import { VisTypeIcon } from './vis_type_icon'; -import { VisType, TypesStart } from '../../types'; +import { VisType, TypesStart } from '../../vis_types'; export interface VisTypeListEntry extends VisType { highlighted: boolean; diff --git a/src/optimize/base_optimizer.js b/src/optimize/base_optimizer.js index 4fabe97e2b3453..f807d8d788a199 100644 --- a/src/optimize/base_optimizer.js +++ b/src/optimize/base_optimizer.js @@ -148,11 +148,7 @@ export default class BaseOptimizer { getThreadLoaderPoolConfig() { // Calculate the node options from the NODE_OPTIONS env var - const parsedNodeOptions = process.env.NODE_OPTIONS - ? // thread-loader could not receive empty string as options - // or it would break that's why we need to filter here - process.env.NODE_OPTIONS.split(/\s/).filter(opt => !!opt) - : []; + const parsedNodeOptions = process.env.NODE_OPTIONS ? process.env.NODE_OPTIONS.split(/\s/) : []; return { name: 'optimizer-thread-loader-main-pool', diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index e1fe7d414756a1..7600bd9db6094b 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -23,12 +23,6 @@ export interface IKibanaSearchResponse { */ id?: string; - /** - * If relevant to the search strategy, return a percentage - * that represents how progress is indicated. - */ - percentComplete?: number; - /** * If relevant to the search strategy, return a total number * that represents how progress is indicated. diff --git a/src/plugins/data/public/search/es_search/es_search_strategy.ts b/src/plugins/data/public/search/es_search/es_search_strategy.ts index a5eab20a89f53d..5382a59123e780 100644 --- a/src/plugins/data/public/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/public/search/es_search/es_search_strategy.ts @@ -26,6 +26,8 @@ import { ISearchContext, TSearchStrategyProvider, ISearchStrategy } from '../typ export const esSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext ): ISearchStrategy => { + const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); + const { search } = syncStrategyProvider(context); return { search: (request, options) => { if (typeof request.params.preference === 'undefined') { @@ -33,11 +35,9 @@ export const esSearchStrategyProvider: TSearchStrategyProvider; + return search({ ...request, serverStrategy: ES_SEARCH_STRATEGY }, options) as Observable< + IEsSearchResponse + >; }, }; }; diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 762c278a05640b..853dbd09e1f939 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -35,7 +35,7 @@ export { export { IEsSearchResponse, IEsSearchRequest, ES_SEARCH_STRATEGY } from '../../common/search'; -export { SYNC_SEARCH_STRATEGY } from './sync_search_strategy'; +export { ISyncSearchRequest, SYNC_SEARCH_STRATEGY } from './sync_search_strategy'; export { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search'; diff --git a/src/plugins/data/public/search/sync_search_strategy.test.ts b/src/plugins/data/public/search/sync_search_strategy.test.ts index 9378e5833f8d73..31a1adfa01c754 100644 --- a/src/plugins/data/public/search/sync_search_strategy.test.ts +++ b/src/plugins/data/public/search/sync_search_strategy.test.ts @@ -35,12 +35,9 @@ describe('Sync search strategy', () => { core: mockCoreStart, getSearchStrategy: jest.fn(), }); - syncSearch.search( - { - serverStrategy: SYNC_SEARCH_STRATEGY, - }, - {} - ); + const request = { serverStrategy: SYNC_SEARCH_STRATEGY }; + syncSearch.search(request, {}); + expect(mockCoreStart.http.fetch.mock.calls[0][0]).toEqual({ path: `/internal/search/${SYNC_SEARCH_STRATEGY}`, body: JSON.stringify({ @@ -50,4 +47,47 @@ describe('Sync search strategy', () => { signal: undefined, }); }); + + it('increments and decrements loading count on success', async () => { + const expectedLoadingCountValues = [0, 1, 0]; + const receivedLoadingCountValues: number[] = []; + + mockCoreStart.http.fetch.mockResolvedValueOnce('response'); + + const syncSearch = syncSearchStrategyProvider({ + core: mockCoreStart, + getSearchStrategy: jest.fn(), + }); + const request = { serverStrategy: SYNC_SEARCH_STRATEGY }; + + const loadingCount$ = mockCoreStart.http.addLoadingCountSource.mock.calls[0][0]; + loadingCount$.subscribe(value => receivedLoadingCountValues.push(value)); + + await syncSearch.search(request, {}).toPromise(); + + expect(receivedLoadingCountValues).toEqual(expectedLoadingCountValues); + }); + + it('increments and decrements loading count on failure', async () => { + expect.assertions(1); + const expectedLoadingCountValues = [0, 1, 0]; + const receivedLoadingCountValues: number[] = []; + + mockCoreStart.http.fetch.mockRejectedValueOnce('error'); + + const syncSearch = syncSearchStrategyProvider({ + core: mockCoreStart, + getSearchStrategy: jest.fn(), + }); + const request = { serverStrategy: SYNC_SEARCH_STRATEGY }; + + const loadingCount$ = mockCoreStart.http.addLoadingCountSource.mock.calls[0][0]; + loadingCount$.subscribe(value => receivedLoadingCountValues.push(value)); + + try { + await syncSearch.search(request, {}).toPromise(); + } catch (e) { + expect(receivedLoadingCountValues).toEqual(expectedLoadingCountValues); + } + }); }); diff --git a/src/plugins/data/public/search/sync_search_strategy.ts b/src/plugins/data/public/search/sync_search_strategy.ts index c2cc180af546ec..860ce593ae217a 100644 --- a/src/plugins/data/public/search/sync_search_strategy.ts +++ b/src/plugins/data/public/search/sync_search_strategy.ts @@ -18,7 +18,8 @@ */ import { BehaviorSubject, from } from 'rxjs'; -import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../common/search'; +import { finalize } from 'rxjs/operators'; +import { IKibanaSearchRequest } from '../../common/search'; import { ISearch, ISearchOptions } from './i_search'; import { TSearchStrategyProvider, ISearchStrategy, ISearchContext } from './types'; @@ -40,16 +41,14 @@ export const syncSearchStrategyProvider: TSearchStrategyProvider { loadingCount$.next(loadingCount$.getValue() + 1); - const response: Promise = context.core.http.fetch({ - path: `/internal/search/${request.serverStrategy}`, - method: 'POST', - body: JSON.stringify(request), - signal: options.signal, - }); - - response.then(() => loadingCount$.next(loadingCount$.getValue() - 1)); - - return from(response); + return from( + context.core.http.fetch({ + path: `/internal/search/${request.serverStrategy}`, + method: 'POST', + body: JSON.stringify(request), + signal: options.signal, + }) + ).pipe(finalize(() => loadingCount$.next(loadingCount$.getValue() - 1))); }; return { search }; diff --git a/src/plugins/data/server/autocomplete/value_suggestions_route.ts b/src/plugins/data/server/autocomplete/value_suggestions_route.ts index f032890e98901b..02a5e0921fe4f3 100644 --- a/src/plugins/data/server/autocomplete/value_suggestions_route.ts +++ b/src/plugins/data/server/autocomplete/value_suggestions_route.ts @@ -23,6 +23,7 @@ import { IRouter } from 'kibana/server'; import { IFieldType, Filter } from '../index'; import { findIndexPatternById, getFieldByName } from '../index_patterns'; +import { getRequestAbortedSignal } from '../lib'; export function registerValueSuggestionsRoute(router: IRouter) { router.post( @@ -50,6 +51,7 @@ export function registerValueSuggestionsRoute(router: IRouter) { const { field: fieldName, query, boolFilter } = request.body; const { index } = request.params; const { dataClient } = context.core.elasticsearch; + const signal = getRequestAbortedSignal(request.events.aborted$); const autocompleteSearchOptions = { timeout: await uiSettings.get('kibana.autocompleteTimeout'), @@ -62,7 +64,7 @@ export function registerValueSuggestionsRoute(router: IRouter) { const body = await getBody(autocompleteSearchOptions, field || fieldName, query, boolFilter); try { - const result = await dataClient.callAsCurrentUser('search', { index, body }); + const result = await dataClient.callAsCurrentUser('search', { index, body }, { signal }); const buckets: any[] = get(result, 'aggregations.suggestions.buckets') || diff --git a/src/plugins/data/server/lib/get_request_aborted_signal.test.ts b/src/plugins/data/server/lib/get_request_aborted_signal.test.ts new file mode 100644 index 00000000000000..3c1e20dbcb1581 --- /dev/null +++ b/src/plugins/data/server/lib/get_request_aborted_signal.test.ts @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Subject } from 'rxjs'; +import { getRequestAbortedSignal } from './get_request_aborted_signal'; + +describe('abortableRequestHandler', () => { + jest.useFakeTimers(); + + it('should call abort if disconnected', () => { + const abortedSubject = new Subject(); + const aborted$ = abortedSubject.asObservable(); + const onAborted = jest.fn(); + + const signal = getRequestAbortedSignal(aborted$); + signal.addEventListener('abort', onAborted); + + // Shouldn't be aborted or call onAborted prior to disconnecting + expect(signal.aborted).toBe(false); + expect(onAborted).not.toBeCalled(); + + abortedSubject.next(); + jest.runAllTimers(); + + // Should be aborted and call onAborted after disconnecting + expect(signal.aborted).toBe(true); + expect(onAborted).toBeCalled(); + }); +}); diff --git a/src/plugins/data/server/lib/get_request_aborted_signal.ts b/src/plugins/data/server/lib/get_request_aborted_signal.ts new file mode 100644 index 00000000000000..d1541f1df93846 --- /dev/null +++ b/src/plugins/data/server/lib/get_request_aborted_signal.ts @@ -0,0 +1,33 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable } from 'rxjs'; +// @ts-ignore not typed +import { AbortController } from 'abortcontroller-polyfill/dist/cjs-ponyfill'; + +/** + * A simple utility function that returns an `AbortSignal` corresponding to an `AbortController` + * which aborts when the given request is aborted. + * @param aborted$ The observable of abort events (usually `request.events.aborted$`) + */ +export function getRequestAbortedSignal(aborted$: Observable): AbortSignal { + const controller = new AbortController(); + aborted$.subscribe(() => controller.abort()); + return controller.signal; +} diff --git a/src/plugins/data/server/lib/index.ts b/src/plugins/data/server/lib/index.ts new file mode 100644 index 00000000000000..a2af456846e146 --- /dev/null +++ b/src/plugins/data/server/lib/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { getRequestAbortedSignal } from './get_request_aborted_signal'; diff --git a/src/plugins/data/server/search/create_api.test.ts b/src/plugins/data/server/search/create_api.test.ts index cc13269e1aa21e..99e48056ef8578 100644 --- a/src/plugins/data/server/search/create_api.test.ts +++ b/src/plugins/data/server/search/create_api.test.ts @@ -25,7 +25,7 @@ import { DEFAULT_SEARCH_STRATEGY } from '../../common/search'; // let mockCoreSetup: MockedKeys; -const mockDefaultSearch = jest.fn(() => Promise.resolve({ percentComplete: 0 })); +const mockDefaultSearch = jest.fn(() => Promise.resolve({ total: 100, loaded: 0 })); const mockDefaultSearchStrategyProvider = jest.fn(() => Promise.resolve({ search: mockDefaultSearch, diff --git a/src/plugins/data/server/search/create_api.ts b/src/plugins/data/server/search/create_api.ts index e1613103ac3998..798a4b82caaefa 100644 --- a/src/plugins/data/server/search/create_api.ts +++ b/src/plugins/data/server/search/create_api.ts @@ -31,7 +31,7 @@ export function createApi({ }) { const api: IRouteHandlerSearchContext = { search: async (request, options, strategyName) => { - const name = strategyName ? strategyName : DEFAULT_SEARCH_STRATEGY; + const name = strategyName ?? DEFAULT_SEARCH_STRATEGY; const strategyProvider = searchStrategies[name]; if (!strategyProvider) { throw new Error(`No strategy found for ${strategyName}`); @@ -40,6 +40,15 @@ export function createApi({ const strategy = await strategyProvider(caller, api.search); return strategy.search(request, options); }, + cancel: async (id, strategyName) => { + const name = strategyName ?? DEFAULT_SEARCH_STRATEGY; + const strategyProvider = searchStrategies[name]; + if (!strategyProvider) { + throw new Error(`No strategy found for ${strategyName}`); + } + const strategy = await strategyProvider(caller, api.search); + return strategy.cancel && strategy.cancel(id); + }, }; return api; } diff --git a/src/plugins/data/server/search/i_route_handler_search_context.ts b/src/plugins/data/server/search/i_route_handler_search_context.ts index 8a44738a1dcfad..89862781b826e7 100644 --- a/src/plugins/data/server/search/i_route_handler_search_context.ts +++ b/src/plugins/data/server/search/i_route_handler_search_context.ts @@ -17,8 +17,9 @@ * under the License. */ -import { ISearchGeneric } from './i_search'; +import { ISearchGeneric, ICancelGeneric } from './i_search'; export interface IRouteHandlerSearchContext { search: ISearchGeneric; + cancel: ICancelGeneric; } diff --git a/src/plugins/data/server/search/i_search.ts b/src/plugins/data/server/search/i_search.ts index 0a357345741534..ea014c5e136d92 100644 --- a/src/plugins/data/server/search/i_search.ts +++ b/src/plugins/data/server/search/i_search.ts @@ -42,7 +42,14 @@ export type ISearchGeneric = Promise; +export type ICancelGeneric = ( + id: string, + strategy?: T +) => Promise; + export type ISearch = ( request: IRequestTypesMap[T], options?: ISearchOptions ) => Promise; + +export type ICancel = (id: string) => Promise; diff --git a/src/plugins/data/server/search/i_search_strategy.ts b/src/plugins/data/server/search/i_search_strategy.ts index d00dd552c9e958..4cfc9608383a90 100644 --- a/src/plugins/data/server/search/i_search_strategy.ts +++ b/src/plugins/data/server/search/i_search_strategy.ts @@ -18,7 +18,7 @@ */ import { APICaller } from 'kibana/server'; -import { ISearch, ISearchGeneric } from './i_search'; +import { ISearch, ICancel, ISearchGeneric } from './i_search'; import { TStrategyTypes } from './strategy_types'; import { ISearchContext } from './i_search_context'; @@ -28,6 +28,7 @@ import { ISearchContext } from './i_search_context'; */ export interface ISearchStrategy { search: ISearch; + cancel?: ICancel; } /** diff --git a/src/plugins/data/server/search/routes.ts b/src/plugins/data/server/search/routes.ts index 6f726771c41b2f..2b8c4b95ee0228 100644 --- a/src/plugins/data/server/search/routes.ts +++ b/src/plugins/data/server/search/routes.ts @@ -19,6 +19,7 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '../../../../core/server'; +import { getRequestAbortedSignal } from '../lib'; export function registerSearchRoute(router: IRouter): void { router.post( @@ -34,9 +35,11 @@ export function registerSearchRoute(router: IRouter): void { }, async (context, request, res) => { const searchRequest = request.body; - const strategy = request.params.strategy; + const { strategy } = request.params; + const signal = getRequestAbortedSignal(request.events.aborted$); + try { - const response = await context.search!.search(searchRequest, {}, strategy); + const response = await context.search!.search(searchRequest, { signal }, strategy); return res.ok({ body: response }); } catch (err) { return res.customError({ @@ -51,4 +54,35 @@ export function registerSearchRoute(router: IRouter): void { } } ); + + router.delete( + { + path: '/internal/search/{strategy}/{id}', + validate: { + params: schema.object({ + strategy: schema.string(), + id: schema.string(), + }), + + query: schema.object({}, { allowUnknowns: true }), + }, + }, + async (context, request, res) => { + const { strategy, id } = request.params; + try { + await context.search!.cancel(id, strategy); + return res.ok(); + } catch (err) { + return res.customError({ + statusCode: err.statusCode, + body: { + message: err.message, + attributes: { + error: err.body.error, + }, + }, + }); + } + } + ); } diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx index 534e1cae3f62de..a1b332bb656174 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx @@ -22,6 +22,7 @@ import { Adapters } from '../types'; import { IContainer } from '../containers'; import { IEmbeddable, EmbeddableInput, EmbeddableOutput } from './i_embeddable'; import { ViewMode } from '../types'; +import { EmbeddableActionStorage } from './embeddable_action_storage'; function getPanelTitle(input: EmbeddableInput, output: EmbeddableOutput) { return input.hidePanelTitles ? '' : input.title === undefined ? output.defaultTitle : input.title; @@ -49,6 +50,11 @@ export abstract class Embeddable< // TODO: Rename to destroyed. private destoyed: boolean = false; + private __actionStorage?: EmbeddableActionStorage; + public get actionStorage(): EmbeddableActionStorage { + return this.__actionStorage || (this.__actionStorage = new EmbeddableActionStorage(this)); + } + constructor(input: TEmbeddableInput, output: TEmbeddableOutput, parent?: IContainer) { this.id = input.id; this.output = { diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts new file mode 100644 index 00000000000000..56facc37fc6661 --- /dev/null +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts @@ -0,0 +1,536 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Embeddable } from './embeddable'; +import { EmbeddableInput } from './i_embeddable'; +import { ViewMode } from '../types'; +import { EmbeddableActionStorage, SerializedEvent } from './embeddable_action_storage'; +import { of } from '../../../../kibana_utils/common'; + +class TestEmbeddable extends Embeddable { + public readonly type = 'test'; + constructor() { + super({ id: 'test', viewMode: ViewMode.VIEW }, {}); + } + reload() {} +} + +describe('EmbeddableActionStorage', () => { + describe('.create()', () => { + test('method exists', () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + expect(typeof storage.create).toBe('function'); + }); + + test('can add event to embeddable', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + const event: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + const events1 = embeddable.getInput().events || []; + expect(events1).toEqual([]); + + await storage.create(event); + + const events2 = embeddable.getInput().events || []; + expect(events2).toEqual([event]); + }); + + test('can create multiple events', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event1: SerializedEvent = { + eventId: 'EVENT_ID1', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + const event2: SerializedEvent = { + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + const event3: SerializedEvent = { + eventId: 'EVENT_ID3', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + const events1 = embeddable.getInput().events || []; + expect(events1).toEqual([]); + + await storage.create(event1); + + const events2 = embeddable.getInput().events || []; + expect(events2).toEqual([event1]); + + await storage.create(event2); + await storage.create(event3); + + const events3 = embeddable.getInput().events || []; + expect(events3).toEqual([event1, event2, event3]); + }); + + test('throws when creating an event with the same ID', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + const event: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + await storage.create(event); + const [, error] = await of(storage.create(event)); + + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatchInlineSnapshot( + `"[EEXIST]: Event with [eventId = EVENT_ID] already exists on [embeddable.id = test, embeddable.title = undefined]."` + ); + }); + }); + + describe('.update()', () => { + test('method exists', () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + expect(typeof storage.update).toBe('function'); + }); + + test('can update an existing event', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event1: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: { + name: 'foo', + } as any, + }; + const event2: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: { + name: 'bar', + } as any, + }; + + await storage.create(event1); + await storage.update(event2); + + const events = embeddable.getInput().events || []; + expect(events).toEqual([event2]); + }); + + test('updates event in place of the old event', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event1: SerializedEvent = { + eventId: 'EVENT_ID1', + triggerId: 'TRIGGER-ID', + action: { + name: 'foo', + } as any, + }; + const event2: SerializedEvent = { + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID', + action: { + name: 'bar', + } as any, + }; + const event22: SerializedEvent = { + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID', + action: { + name: 'baz', + } as any, + }; + const event3: SerializedEvent = { + eventId: 'EVENT_ID3', + triggerId: 'TRIGGER-ID', + action: { + name: 'qux', + } as any, + }; + + await storage.create(event1); + await storage.create(event2); + await storage.create(event3); + + const events1 = embeddable.getInput().events || []; + expect(events1).toEqual([event1, event2, event3]); + + await storage.update(event22); + + const events2 = embeddable.getInput().events || []; + expect(events2).toEqual([event1, event22, event3]); + + await storage.update(event2); + + const events3 = embeddable.getInput().events || []; + expect(events3).toEqual([event1, event2, event3]); + }); + + test('throws when updating event, but storage is empty', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + const [, error] = await of(storage.update(event)); + + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatchInlineSnapshot( + `"[ENOENT]: Event with [eventId = EVENT_ID] could not be updated as it does not exist in [embeddable.id = test, embeddable.title = undefined]."` + ); + }); + + test('throws when updating event with ID that is not stored', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event1: SerializedEvent = { + eventId: 'EVENT_ID1', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + const event2: SerializedEvent = { + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + await storage.create(event1); + const [, error] = await of(storage.update(event2)); + + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatchInlineSnapshot( + `"[ENOENT]: Event with [eventId = EVENT_ID2] could not be updated as it does not exist in [embeddable.id = test, embeddable.title = undefined]."` + ); + }); + }); + + describe('.remove()', () => { + test('method exists', () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + expect(typeof storage.remove).toBe('function'); + }); + + test('can remove existing event', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + await storage.create(event); + await storage.remove(event.eventId); + + const events = embeddable.getInput().events || []; + expect(events).toEqual([]); + }); + + test('removes correct events in a list of events', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event1: SerializedEvent = { + eventId: 'EVENT_ID1', + triggerId: 'TRIGGER-ID', + action: { + name: 'foo', + } as any, + }; + const event2: SerializedEvent = { + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID', + action: { + name: 'bar', + } as any, + }; + const event3: SerializedEvent = { + eventId: 'EVENT_ID3', + triggerId: 'TRIGGER-ID', + action: { + name: 'qux', + } as any, + }; + + await storage.create(event1); + await storage.create(event2); + await storage.create(event3); + + const events1 = embeddable.getInput().events || []; + expect(events1).toEqual([event1, event2, event3]); + + await storage.remove(event2.eventId); + + const events2 = embeddable.getInput().events || []; + expect(events2).toEqual([event1, event3]); + + await storage.remove(event3.eventId); + + const events3 = embeddable.getInput().events || []; + expect(events3).toEqual([event1]); + + await storage.remove(event1.eventId); + + const events4 = embeddable.getInput().events || []; + expect(events4).toEqual([]); + }); + + test('throws when removing an event from an empty storage', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const [, error] = await of(storage.remove('EVENT_ID')); + + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatchInlineSnapshot( + `"[ENOENT]: Event with [eventId = EVENT_ID] could not be removed as it does not exist in [embeddable.id = test, embeddable.title = undefined]."` + ); + }); + + test('throws when removing with ID that does not exist in storage', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + await storage.create(event); + const [, error] = await of(storage.remove('WRONG_ID')); + await storage.remove(event.eventId); + + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatchInlineSnapshot( + `"[ENOENT]: Event with [eventId = WRONG_ID] could not be removed as it does not exist in [embeddable.id = test, embeddable.title = undefined]."` + ); + }); + }); + + describe('.read()', () => { + test('method exists', () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + expect(typeof storage.read).toBe('function'); + }); + + test('can read an existing event out of storage', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + await storage.create(event); + const event2 = await storage.read(event.eventId); + + expect(event2).toEqual(event); + }); + + test('throws when reading from empty storage', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const [, error] = await of(storage.read('EVENT_ID')); + + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatchInlineSnapshot( + `"[ENOENT]: Event with [eventId = EVENT_ID] could not be found in [embeddable.id = test, embeddable.title = undefined]."` + ); + }); + + test('throws when reading event with ID not existing in storage', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event: SerializedEvent = { + eventId: 'EVENT_ID', + triggerId: 'TRIGGER-ID', + action: {} as any, + }; + + await storage.create(event); + const [, error] = await of(storage.read('WRONG_ID')); + + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatchInlineSnapshot( + `"[ENOENT]: Event with [eventId = WRONG_ID] could not be found in [embeddable.id = test, embeddable.title = undefined]."` + ); + }); + + test('returns correct event when multiple events are stored', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event1: SerializedEvent = { + eventId: 'EVENT_ID1', + triggerId: 'TRIGGER-ID1', + action: {} as any, + }; + const event2: SerializedEvent = { + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID2', + action: {} as any, + }; + const event3: SerializedEvent = { + eventId: 'EVENT_ID3', + triggerId: 'TRIGGER-ID3', + action: {} as any, + }; + + await storage.create(event1); + await storage.create(event2); + await storage.create(event3); + + const event12 = await storage.read(event1.eventId); + const event22 = await storage.read(event2.eventId); + const event32 = await storage.read(event3.eventId); + + expect(event12).toEqual(event1); + expect(event22).toEqual(event2); + expect(event32).toEqual(event3); + + expect(event12).not.toEqual(event2); + }); + }); + + describe('.count()', () => { + test('method exists', () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + expect(typeof storage.count).toBe('function'); + }); + + test('returns 0 when storage is empty', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const count = await storage.count(); + + expect(count).toBe(0); + }); + + test('returns correct number of events in storage', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + expect(await storage.count()).toBe(0); + + await storage.create({ + eventId: 'EVENT_ID1', + triggerId: 'TRIGGER-ID1', + action: {} as any, + }); + + expect(await storage.count()).toBe(1); + + await storage.create({ + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID1', + action: {} as any, + }); + + expect(await storage.count()).toBe(2); + + await storage.remove('EVENT_ID1'); + + expect(await storage.count()).toBe(1); + + await storage.remove('EVENT_ID2'); + + expect(await storage.count()).toBe(0); + }); + }); + + describe('.list()', () => { + test('method exists', () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + expect(typeof storage.list).toBe('function'); + }); + + test('returns empty array when storage is empty', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const list = await storage.list(); + + expect(list).toEqual([]); + }); + + test('returns correct list of events in storage', async () => { + const embeddable = new TestEmbeddable(); + const storage = new EmbeddableActionStorage(embeddable); + + const event1: SerializedEvent = { + eventId: 'EVENT_ID1', + triggerId: 'TRIGGER-ID1', + action: {} as any, + }; + + const event2: SerializedEvent = { + eventId: 'EVENT_ID2', + triggerId: 'TRIGGER-ID1', + action: {} as any, + }; + + expect(await storage.list()).toEqual([]); + + await storage.create(event1); + + expect(await storage.list()).toEqual([event1]); + + await storage.create(event2); + + expect(await storage.list()).toEqual([event1, event2]); + + await storage.remove('EVENT_ID1'); + + expect(await storage.list()).toEqual([event2]); + + await storage.remove('EVENT_ID2'); + + expect(await storage.list()).toEqual([]); + }); + }); +}); diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.ts new file mode 100644 index 00000000000000..520f92840c5f9c --- /dev/null +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.ts @@ -0,0 +1,126 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Embeddable } from '..'; + +/** + * Below two interfaces are here temporarily, they will move to `ui_actions` + * plugin once #58216 is merged. + */ +export interface SerializedEvent { + eventId: string; + triggerId: string; + action: unknown; +} +export interface ActionStorage { + create(event: SerializedEvent): Promise; + update(event: SerializedEvent): Promise; + remove(eventId: string): Promise; + read(eventId: string): Promise; + count(): Promise; + list(): Promise; +} + +export class EmbeddableActionStorage implements ActionStorage { + constructor(private readonly embbeddable: Embeddable) {} + + async create(event: SerializedEvent) { + const input = this.embbeddable.getInput(); + const events = (input.events || []) as SerializedEvent[]; + const exists = !!events.find(({ eventId }) => eventId === event.eventId); + + if (exists) { + throw new Error( + `[EEXIST]: Event with [eventId = ${event.eventId}] already exists on ` + + `[embeddable.id = ${input.id}, embeddable.title = ${input.title}].` + ); + } + + this.embbeddable.updateInput({ + ...input, + events: [...events, event], + }); + } + + async update(event: SerializedEvent) { + const input = this.embbeddable.getInput(); + const events = (input.events || []) as SerializedEvent[]; + const index = events.findIndex(({ eventId }) => eventId === event.eventId); + + if (index === -1) { + throw new Error( + `[ENOENT]: Event with [eventId = ${event.eventId}] could not be ` + + `updated as it does not exist in ` + + `[embeddable.id = ${input.id}, embeddable.title = ${input.title}].` + ); + } + + this.embbeddable.updateInput({ + ...input, + events: [...events.slice(0, index), event, ...events.slice(index + 1)], + }); + } + + async remove(eventId: string) { + const input = this.embbeddable.getInput(); + const events = (input.events || []) as SerializedEvent[]; + const index = events.findIndex(event => eventId === event.eventId); + + if (index === -1) { + throw new Error( + `[ENOENT]: Event with [eventId = ${eventId}] could not be ` + + `removed as it does not exist in ` + + `[embeddable.id = ${input.id}, embeddable.title = ${input.title}].` + ); + } + + this.embbeddable.updateInput({ + ...input, + events: [...events.slice(0, index), ...events.slice(index + 1)], + }); + } + + async read(eventId: string): Promise { + const input = this.embbeddable.getInput(); + const events = (input.events || []) as SerializedEvent[]; + const event = events.find(ev => eventId === ev.eventId); + + if (!event) { + throw new Error( + `[ENOENT]: Event with [eventId = ${eventId}] could not be found in ` + + `[embeddable.id = ${input.id}, embeddable.title = ${input.title}].` + ); + } + + return event; + } + + private __list() { + const input = this.embbeddable.getInput(); + return (input.events || []) as SerializedEvent[]; + } + + async count(): Promise { + return this.__list().length; + } + + async list(): Promise { + return this.__list(); + } +} diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts index 46cffab8796846..62121cb0f23dd5 100644 --- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts +++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts @@ -30,6 +30,11 @@ export interface EmbeddableInput { hidePanelTitles?: boolean; isEmptyState?: boolean; + /** + * Reserved key for `ui_actions` events. + */ + events?: unknown; + /** * List of action IDs that this embeddable should not render. */ diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index f6ff9efca848b9..4776204a8ab2ff 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -630,6 +630,7 @@ describe('Execution', () => { }, }); expect(node2.debug?.rawError).toBeInstanceOf(Error); + expect(node2.debug?.rawError).toEqual(new Error('foo')); }); test('sets .debug object to expected shape', async () => { diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 7e7df822724aea..f70a32f2f09c12 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -21,7 +21,7 @@ import { keys, last, mapValues, reduce, zipObject } from 'lodash'; import { Executor } from '../executor'; import { createExecutionContainer, ExecutionContainer } from './container'; import { createError } from '../util'; -import { Defer } from '../../../kibana_utils/common'; +import { Defer, now } from '../../../kibana_utils/common'; import { RequestAdapter, DataAdapter } from '../../../inspector/common'; import { isExpressionValueError, ExpressionValueError } from '../expression_types/specs/error'; import { @@ -211,11 +211,11 @@ export class Execution< // actually have a `then` function which would be treated as a `Promise`. const { resolvedArgs } = await this.resolveArgs(fn, input, fnArgs); args = resolvedArgs; - timeStart = this.params.debug ? performance.now() : 0; + timeStart = this.params.debug ? now() : 0; const output = await this.invokeFunction(fn, input, resolvedArgs); if (this.params.debug) { - const timeEnd: number = performance.now(); + const timeEnd: number = now(); (link as ExpressionAstFunction).debug = { success: true, fn, @@ -229,9 +229,9 @@ export class Execution< if (getType(output) === 'error') return output; input = output; } catch (rawError) { - const timeEnd: number = this.params.debug ? performance.now() : 0; - rawError.message = `[${fnName}] > ${rawError.message}`; + const timeEnd: number = this.params.debug ? now() : 0; const error = createError(rawError) as ExpressionValueError; + error.error.message = `[${fnName}] > ${error.error.message}`; if (this.params.debug) { (link as ExpressionAstFunction).debug = { diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index 4600922e076faa..fbe2f37c648d6a 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -167,7 +167,7 @@ export class ExpressionLoader { }; private render(data: Data): void { - this.renderHandler.render(data, this.params.extraHandlers); + this.renderHandler.render(data, this.params.uiState); } private setParams(params?: IExpressionLoaderParams) { @@ -182,8 +182,8 @@ export class ExpressionLoader { this.params.searchContext || {} ) as any; } - if (params.extraHandlers && this.params) { - this.params.extraHandlers = params.extraHandlers; + if (params.uiState && this.params) { + this.params.uiState = params.uiState; } if (params.variables && this.params) { this.params.variables = params.variables; diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts index 86e360f8135e7f..ad4d16bcd13234 100644 --- a/src/plugins/expressions/public/render.ts +++ b/src/plugins/expressions/public/render.ts @@ -94,7 +94,7 @@ export class ExpressionRenderHandler { }; } - render = async (data: any, extraHandlers: IExpressionRendererExtraHandlers = {}) => { + render = async (data: any, uiState: any = {}) => { if (!data || typeof data !== 'object') { return this.handleRenderError(new Error('invalid data provided to the expression renderer')); } @@ -119,7 +119,7 @@ export class ExpressionRenderHandler { .get(data.as)! .render(this.element, data.value, { ...this.handlers, - ...extraHandlers, + uiState, } as any); } catch (e) { return this.handleRenderError(e); diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index c77698d3661c24..b5781ef213fd0a 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -48,7 +48,7 @@ export interface IExpressionLoaderParams { disableCaching?: boolean; customFunctions?: []; customRenderers?: []; - extraHandlers?: Record; + uiState?: unknown; inspectorAdapters?: Adapters; onRenderError?: RenderErrorHandlerFnType; } diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts index 3b07674315dce3..50120edc0c0561 100644 --- a/src/plugins/kibana_utils/common/index.ts +++ b/src/plugins/kibana_utils/common/index.ts @@ -25,3 +25,4 @@ export * from './typed_json'; export { createGetterSetter, Get, Set } from './create_getter_setter'; export { distinctUntilChangedWithInitialValue } from './distinct_until_changed_with_initial_value'; export { url } from './url'; +export { now } from './now'; diff --git a/src/plugins/kibana_utils/common/now.ts b/src/plugins/kibana_utils/common/now.ts new file mode 100644 index 00000000000000..e6c6d745526226 --- /dev/null +++ b/src/plugins/kibana_utils/common/now.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Function that returns number in milliseconds since some undefined point in + * time. Use this function for performance measurements. + */ +export const now: () => number = + typeof performance === 'object' ? performance.now.bind(performance) : Date.now; diff --git a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx index da70d9fe895255..275a9da96a2c4d 100644 --- a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx +++ b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx @@ -39,7 +39,11 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../legacy/core_plugins/visualizations/public/embeddable/constants'; + +// TODO: can't import from '../../../../legacy/core_plugins/visualizations/public/' directly, +// because yarn build:types fails after trying to emit type declarations for whole visualizations plugin +// Bunch of errors like this: 'Return type of exported function has or is using private name 'SavedVis'' +import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../legacy/core_plugins/visualizations/public/np_ready/public/embeddable/constants'; export interface OnSaveProps { newTitle: string; diff --git a/test/functional/apps/visualize/_linked_saved_searches.js b/test/functional/apps/visualize/_linked_saved_searches.ts similarity index 95% rename from test/functional/apps/visualize/_linked_saved_searches.js rename to test/functional/apps/visualize/_linked_saved_searches.ts index 37ec3f06f2ecd0..345987a803394a 100644 --- a/test/functional/apps/visualize/_linked_saved_searches.js +++ b/test/functional/apps/visualize/_linked_saved_searches.ts @@ -18,8 +18,9 @@ */ import expect from '@kbn/expect'; - -export default function({ getService, getPageObjects }) { +import { FtrProviderContext } from '../../ftr_provider_context'; +// eslint-disable-next-line import/no-default-export +export default function({ getService, getPageObjects }: FtrProviderContext) { const filterBar = getService('filterBar'); const retry = getService('retry'); const PageObjects = getPageObjects([ @@ -40,8 +41,6 @@ export default function({ getService, getPageObjects }) { await filterBar.addFilter('extension.raw', 'is', 'jpg'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.saveSearch(savedSearchName); - // TODO: Remove this once https://github.com/elastic/kibana/issues/19750 is properly resolved - await PageObjects.common.sleep(500); }); it('should create a visualization from a saved search', async () => { diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index f22f7e98d3b8a4..b1cb9075434e87 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -16,7 +16,7 @@ "xpack.fileUpload": "legacy/plugins/file_upload", "xpack.graph": ["legacy/plugins/graph", "plugins/graph"], "xpack.grokDebugger": "legacy/plugins/grokdebugger", - "xpack.idxMgmt": "legacy/plugins/index_management", + "xpack.idxMgmt": "plugins/index_management", "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management", "xpack.infra": "plugins/infra", "xpack.ingestManager": "plugins/ingest_manager", diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx index e8e37cfdfb1f0f..be92b119d58566 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx @@ -26,12 +26,15 @@ export function Popover({ focusedServiceName }: PopoverProps) { const [selectedNode, setSelectedNode] = useState< cytoscape.NodeSingular | undefined >(undefined); - const deselect = useCallback(() => setSelectedNode(undefined), [ - setSelectedNode - ]); + const deselect = useCallback(() => { + if (cy) { + cy.elements().unselect(); + } + setSelectedNode(undefined); + }, [cy, setSelectedNode]); const renderedHeight = selectedNode?.renderedHeight() ?? 0; const renderedWidth = selectedNode?.renderedWidth() ?? 0; - const { x, y } = selectedNode?.renderedPosition() ?? { x: 0, y: 0 }; + const { x, y } = selectedNode?.renderedPosition() ?? { x: -10000, y: -10000 }; const isOpen = !!selectedNode; const isService = selectedNode?.data('type') === 'service'; const triggerStyle: CSSProperties = { diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx index 496147b02589bc..1564f1ae746a99 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx @@ -51,12 +51,18 @@ async function deleteConfig( ) { try { await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/{configurationId}', + pathname: '/api/apm/settings/agent-configuration', method: 'DELETE', params: { - path: { configurationId: selectedConfig.id } + body: { + service: { + name: selectedConfig.service.name, + environment: selectedConfig.service.environment + } + } } }); + toasts.addSuccess({ title: i18n.translate( 'xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededTitle', diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx index 653dedea733f26..c77617fbb424f0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx @@ -135,8 +135,8 @@ export function AddEditFlyout({ sampleRate, captureBody, transactionMaxSpans, - configurationId: selectedConfig ? selectedConfig.id : undefined, agentName, + isExistingConfig: Boolean(selectedConfig), toasts, trackApmEvent }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts index 19934cafb4694f..d36120a0547959 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts @@ -27,8 +27,8 @@ export async function saveConfig({ sampleRate, captureBody, transactionMaxSpans, - configurationId, agentName, + isExistingConfig, toasts, trackApmEvent }: { @@ -38,8 +38,8 @@ export async function saveConfig({ sampleRate: string; captureBody: string; transactionMaxSpans: string; - configurationId?: string; agentName?: string; + isExistingConfig: boolean; toasts: NotificationsStart['toasts']; trackApmEvent: UiTracker; }) { @@ -64,24 +64,14 @@ export async function saveConfig({ settings }; - if (configurationId) { - await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/{configurationId}', - method: 'PUT', - params: { - path: { configurationId }, - body: configuration - } - }); - } else { - await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/new', - method: 'POST', - params: { - body: configuration - } - }); - } + await callApmApi({ + pathname: '/api/apm/settings/agent-configuration', + method: 'PUT', + params: { + query: { overwrite: isExistingConfig }, + body: configuration + } + }); toasts.addSuccess({ title: i18n.translate( diff --git a/x-pack/legacy/plugins/apm/readme.md b/x-pack/legacy/plugins/apm/readme.md index 4892aa1b15bc74..a513249c296db3 100644 --- a/x-pack/legacy/plugins/apm/readme.md +++ b/x-pack/legacy/plugins/apm/readme.md @@ -8,7 +8,7 @@ git clone git@github.com:elastic/kibana.git cd kibana/ yarn kbn bootstrap -yarn start +yarn start --no-base-path ``` #### APM Server, Elasticsearch and data @@ -17,8 +17,7 @@ To access an elasticsearch instance that has live data you have two options: ##### A. Connect to Elasticsearch on Cloud (internal devs only) -Add the following to the kibana config file (config/kibana.dev.yml): -https://p.elstc.co/paste/wcp7hGUC#UkNNwAZg6cCasmUQNMw8ZXntSPuau5FMXCJkSnsvXU+ +Find the credentials for the cluster [here](https://github.com/elastic/apm-dev/blob/master/docs/credentials/apm-ui-clusters.md#apmelstcco) ##### B. Start Elastic Stack and APM data generators @@ -72,9 +71,27 @@ node scripts/jest.js plugins/apm --watch node scripts/jest.js plugins/apm --updateSnapshot ``` -### Cypress E2E tests +### Functional tests -See the Cypress-specific [readme.md](cypress/README.md) +**Start server** +`node scripts/functional_tests_server --config x-pack/test/functional/config.js` + +**Run tests** +`node scripts/functional_test_runner --config x-pack/test/functional/config.js --grep='APM specs'` + +APM tests are located in `x-pack/test/functional/apps/apm`. +For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme) + +### API integration tests + +**Start server** +`node scripts/functional_tests_server --config x-pack/test/api_integration/config.js` + +**Run tests** +`node scripts/functional_test_runner --config x-pack/test/api_integration/config.js --grep='APM specs'` + +APM tests are located in `x-pack/test/api_integration/apis/apm`. +For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme) ### Linting @@ -92,58 +109,6 @@ yarn prettier "./x-pack/legacy/plugins/apm/**/*.{tsx,ts,js}" --write yarn eslint ./x-pack/legacy/plugins/apm --fix ``` -### Visual Studio Code - -When using [Visual Studio Code](https://code.visualstudio.com/) with APM it's best to set up a [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) and add the `x-pack/legacy/plugins/apm` directory, the `x-pack` directory, and the root of the Kibana repository to the workspace. This makes it so you can navigate and search within APM and use the wider workspace roots when you need to widen your search. - -#### Using the Jest extension - -The [vscode-jest extension](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest) is a good way to run your Jest tests inside the editor. - -Some of the benefits of using the extension over just running it in a terminal are: - -• It shows the pass/fail of a test inline in the test file -• It shows the error message in the test file if it fails -• You don’t have to have the terminal process running -• It can automatically update your snapshots when they change -• Coverage mapping - -The extension doesn't really work well if you're trying to use it on all of Kibana or all of X-Pack, but it works well if you configure it to run only on the files in APM. - -If you have a workspace configured as described above you should have: - -```json -"jest.disabledWorkspaceFolders": ["kibana", "x-pack"] -``` - -in your Workspace settings, and: - -```json -"jest.pathToJest": "node scripts/jest.js --testPathPattern=legacy/plugins/apm", -"jest.rootPath": "../../.." -``` - -in the settings for the APM folder. - -#### Jest debugging - -To make the [VSCode debugger](https://vscode.readthedocs.io/en/latest/editor/debugging/) work with Jest (you can set breakpoints in the code and tests and use the VSCode debugger) you'll need the [Node Debug extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.node-debug2) installed and can set up a launch configuration like: - -```json -{ - "type": "node", - "name": "APM Jest", - "request": "launch", - "args": ["--runInBand", "--testPathPattern=legacy/plugins/apm"], - "cwd": "${workspaceFolder}/../../..", - "console": "internalConsole", - "internalConsoleOptions": "openOnSessionStart", - "disableOptimisticBPs": true, - "program": "${workspaceFolder}/../../../scripts/jest.js", - "runtimeVersion": "10.15.2" -} -``` - #### Storybook Start the [Storybook](https://storybook.js.org/) development environment with @@ -152,4 +117,6 @@ You can access the development environment at http://localhost:9001. #### Further resources -(you'll want `runtimeVersion` to match what's in the Kibana root .nvmrc. Depending on your setup, you might be able to remove this line.) +- [Cypress integration tests](cypress/README.md) +- [VSCode setup instructions](./dev_docs/vscode_setup.md) +- [Github PR commands](./dev_docs/github_commands.md) diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts index cc92d864282e74..d9e841092be56b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts @@ -6,7 +6,7 @@ // @ts-ignore import { MAP_SAVED_OBJECT_TYPE } from '../../../maps/common/constants'; -import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/visualizations/public/embeddable/constants'; +import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/visualizations/public'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants'; export const EmbeddableTypes: { map: string; search: string; visualization: string } = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts index 02efb1396486e3..d8ca2ab595872a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunctionDefinition } from 'src/plugins/expressions'; -import { VisualizeInput } from 'src/legacy/core_plugins/visualizations/public/embeddable'; +import { VisualizeInput } from 'src/legacy/core_plugins/visualizations/public'; import { EmbeddableTypes, EmbeddableExpressionType, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.test.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.test.ts index 93d747537c34c1..8694c0e2c7f9f3 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.test.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.test.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +jest.mock('ui/new_platform'); import { embeddableInputToExpression } from './embeddable_input_to_expression'; import { SavedMapInput } from '../../functions/common/saved_map'; import { EmbeddableTypes } from '../../expression_types'; diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx new file mode 100644 index 00000000000000..5907c932ddabb1 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + TODO: uncomment and fix this test to address storybook errors as a result of nested component dependencies - https://github.com/elastic/kibana/issues/58289 + */ + +/* +import { action } from '@storybook/addon-actions'; +import { storiesOf } from '@storybook/react'; +import React from 'react'; +import { Toolbar } from '../toolbar'; + +storiesOf('components/Toolbar', module) + .addDecorator(story => ( +
+ {story()} +
+ )) + .add('with null metric', () => ( + + )); +*/ diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js b/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js index c834304739a4c0..294a44ba0415a6 100644 --- a/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js @@ -7,12 +7,14 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { pure, compose, withState, getContext, withHandlers } from 'recompose'; +import { canUserWrite } from '../../state/selectors/app'; import { getWorkpad, getWorkpadName, getSelectedPageIndex, getSelectedElement, + isWriteable, } from '../../state/selectors/workpad'; import { Toolbar as Component } from './toolbar'; @@ -23,6 +25,7 @@ const mapStateToProps = state => ({ totalPages: getWorkpad(state).pages.length, selectedPageNumber: getSelectedPageIndex(state) + 1, selectedElement: getSelectedElement(state), + isWriteable: isWriteable(state) && canUserWrite(state), }); export const Toolbar = compose( diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx b/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx index 089f021ccdc325..0f8204e6bc261c 100644 --- a/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx @@ -39,7 +39,8 @@ enum TrayType { interface Props { workpadName: string; - + isWriteable: boolean; + canUserWrite: boolean; tray: TrayType | null; setTray: (tray: TrayType | null) => void; @@ -66,12 +67,17 @@ export const Toolbar = (props: Props) => { totalPages, showWorkpadManager, setShowWorkpadManager, + isWriteable, } = props; const elementIsSelected = Boolean(selectedElement); const done = () => setTray(null); + if (!isWriteable && tray === TrayType.expression) { + done(); + } + const showHideTray = (exp: TrayType) => { if (tray && tray === exp) { return done(); @@ -135,7 +141,7 @@ export const Toolbar = (props: Props) => { />
- {elementIsSelected && ( + {elementIsSelected && isWriteable && ( { return get(index, propertyPath); }, @@ -19,4 +20,6 @@ export const followerBadgeExtension = { filterExpression: 'isFollowerIndex:true', }; -extensionsService.addBadge(followerBadgeExtension); +if (npSetup.plugins.indexManagement) { + npSetup.plugins.indexManagement.extensionsService.addBadge(followerBadgeExtension); +} diff --git a/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js b/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js index 0ee4f76ebf9d0c..67086883a9a326 100644 --- a/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js +++ b/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js @@ -268,6 +268,10 @@ export class JsonIndexFilePicker extends Component { maxFileSize: bytesToSize(MAX_FILE_SIZE), }} /> +
+ {i18n.translate('xpack.fileUpload.jsonIndexFilePicker.coordinateSystemAccepted', { + defaultMessage: 'Coordinates must be in EPSG:4326 coordinate reference system.', + })}{' '} ) } diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js index bcbae7e093f465..d2619778617c3e 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js @@ -27,8 +27,10 @@ initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path); initUiMetric(() => () => {}); jest.mock('ui/new_platform'); -jest.mock('../../index_management/public', async () => { - const { indexManagementMock } = await import('../../index_management/public/mocks.ts'); +jest.mock('../../../../plugins/index_management/public', async () => { + const { indexManagementMock } = await import( + '../../../../plugins/index_management/public/mocks.ts' + ); return indexManagementMock.createSetup(); }); diff --git a/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts b/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts index 8d7f937039203f..38d1bea45ce070 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts +++ b/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts @@ -41,14 +41,14 @@ export class Plugin { registerPoliciesRoutes(server); registerTemplatesRoutes(server); - const serverPlugins = server.plugins as any; + const serverPlugins = server.newPlatform.setup.plugins as any; if ( server.config().get('xpack.ilm.ui.enabled') && - serverPlugins.index_management && - serverPlugins.index_management.addIndexManagementDataEnricher + serverPlugins.indexManagement && + serverPlugins.indexManagement.indexDataEnricher ) { - serverPlugins.index_management.addIndexManagementDataEnricher(indexLifecycleDataEnricher); + serverPlugins.indexManagement.indexDataEnricher.add(indexLifecycleDataEnricher); } } } diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts b/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts index 3c21a644c0f783..f7f924add2c514 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts @@ -20,7 +20,9 @@ import { addAllExtensions } from './np_ready/extend_index_management'; if (chrome.getInjected('ilmUiEnabled')) { // We have to initialize this outside of the NP lifecycle, otherwise these extensions won't // be available in Index Management unless the user visits ILM first. - addAllExtensions(); + if ((npSetup.plugins as any).indexManagement) { + addAllExtensions((npSetup.plugins as any).indexManagement.extensionsService); + } // This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular // from destroying scope when route changes and both old route and new route are this same route. diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js index 83d32eae1097d7..903161fe094fc3 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js @@ -37,7 +37,7 @@ import { import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; -import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation'; +import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public'; import { BASE_PATH } from '../../../../../../../common/constants'; import { UIM_EDIT_CLICK } from '../../../../constants'; import { getPolicyPath } from '../../../../services/navigation'; diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js index 0e662b78b2c180..69658d31695bce 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js @@ -9,7 +9,6 @@ import { get, every, any } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiSearchBar } from '@elastic/eui'; -import { extensionsService } from '../../../../index_management/public'; import { init as initUiMetric } from '../application/services/ui_metric'; import { init as initNotification } from '../application/services/notification'; import { retryLifecycleForIndex } from '../application/services/api'; @@ -238,7 +237,7 @@ export const ilmFilterExtension = indices => { } }; -export const addAllExtensions = () => { +export const addAllExtensions = extensionsService => { extensionsService.addAction(retryLifecycleActionExtension); extensionsService.addAction(removeLifecyclePolicyActionExtension); extensionsService.addAction(addLifecyclePolicyActionExtension); diff --git a/x-pack/legacy/plugins/index_management/index.ts b/x-pack/legacy/plugins/index_management/index.ts index c92b38c0d94be8..9eba98a526d2bc 100644 --- a/x-pack/legacy/plugins/index_management/index.ts +++ b/x-pack/legacy/plugins/index_management/index.ts @@ -4,36 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { resolve } from 'path'; -import { Legacy } from 'kibana'; -import { PLUGIN } from './common/constants'; -import { plugin as initServerPlugin, Dependencies } from './server'; - -export type ServerFacade = Legacy.Server; - export function indexManagement(kibana: any) { return new kibana.Plugin({ - id: PLUGIN.id, + id: 'index_management', configPrefix: 'xpack.index_management', - publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'elasticsearch', 'xpack_main'], - - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - managementSections: ['plugins/index_management'], - }, - - init(server: ServerFacade) { - const coreSetup = server.newPlatform.setup.core; - const coreInitializerContext = server.newPlatform.coreContext; - const pluginsSetup: Dependencies = { - licensing: server.newPlatform.setup.plugins.licensing as any, - }; - - const serverPlugin = initServerPlugin(coreInitializerContext as any); - const serverPublicApi = serverPlugin.setup(coreSetup, pluginsSetup); - - server.expose('addIndexManagementDataEnricher', serverPublicApi.indexDataEnricher.add); - }, }); } diff --git a/x-pack/legacy/plugins/index_management/public/index.html b/x-pack/legacy/plugins/index_management/public/index.html deleted file mode 100644 index 0e9ac6fa0bc977..00000000000000 --- a/x-pack/legacy/plugins/index_management/public/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap index 486a830d21b65e..b5fe334f8415e7 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap @@ -17,25 +17,6 @@ exports[`TooltipHeader multiple features, multiple layers: locked should show pa pageCount={3} />
- - - - - @@ -139,25 +120,6 @@ exports[`TooltipHeader multiple features, single layer: locked should show pagin pageCount={2} /> - - - - - 1) { + if (!isLocked && filteredFeatures.length > 1) { headerItems.push( diff --git a/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts b/x-pack/legacy/plugins/ml/common/constants/calendars.ts similarity index 61% rename from x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts rename to x-pack/legacy/plugins/ml/common/constants/calendars.ts index 4c73ec53af9b9b..1a56257ca13049 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts +++ b/x-pack/legacy/plugins/ml/common/constants/calendars.ts @@ -4,10 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const parseFilterQuery = (query?: string | null) => { - try { - return query ? JSON.parse(query) : null; - } catch { - return null; - } -}; +export const GLOBAL_CALENDAR = '_all'; diff --git a/x-pack/legacy/plugins/ml/common/types/file_datavisualizer.ts b/x-pack/legacy/plugins/ml/common/types/file_datavisualizer.ts new file mode 100644 index 00000000000000..bc03f82673a1fe --- /dev/null +++ b/x-pack/legacy/plugins/ml/common/types/file_datavisualizer.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface FindFileStructureResponse { + charset: string; + has_header_row: boolean; + has_byte_order_marker: boolean; + format: string; + field_stats: { + [fieldName: string]: { + count: number; + cardinality: number; + top_hits: Array<{ count: number; value: any }>; + }; + }; + sample_start: string; + num_messages_analyzed: number; + mappings: { + [fieldName: string]: { + type: string; + }; + }; + quote: string; + delimiter: string; + need_client_timezone: boolean; + num_lines_analyzed: number; + column_names: string[]; +} diff --git a/x-pack/legacy/plugins/ml/public/application/app.tsx b/x-pack/legacy/plugins/ml/public/application/app.tsx index 24cbfbfb346dd5..add27193deb77d 100644 --- a/x-pack/legacy/plugins/ml/public/application/app.tsx +++ b/x-pack/legacy/plugins/ml/public/application/app.tsx @@ -12,6 +12,7 @@ import 'ace'; import { AppMountParameters, CoreStart } from 'kibana/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { SecurityPluginSetup } from '../../../../../plugins/security/public'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { setDependencyCache, clearCache } from './util/dependency_cache'; @@ -20,6 +21,7 @@ import { MlRouter } from './routing'; export interface MlDependencies extends AppMountParameters { data: DataPublicPluginStart; + security: SecurityPluginSetup; __LEGACY: { XSRF: string; APP_URL: string; @@ -49,6 +51,7 @@ const App: FC = ({ coreStart, deps }) => { APP_URL: deps.__LEGACY.APP_URL, application: coreStart.application, http: coreStart.http, + security: deps.security, }); deps.onAppLeave(actions => { clearCache(); @@ -64,6 +67,7 @@ const App: FC = ({ coreStart, deps }) => { const services = { appName: 'ML', data: deps.data, + security: deps.security, ...coreStart, }; diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts index aaf539322809b9..5fcd7c5473d3bc 100644 --- a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts +++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts @@ -10,9 +10,11 @@ import { useKibana, KibanaReactContextValue, } from '../../../../../../../../src/plugins/kibana_react/public'; +import { SecurityPluginSetup } from '../../../../../../../plugins/security/public'; interface StartPlugins { data: DataPublicPluginStart; + security: SecurityPluginSetup; } export type StartServices = CoreStart & StartPlugins; // eslint-disable-next-line react-hooks/rules-of-hooks diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts new file mode 100644 index 00000000000000..3344cdf991e6b9 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; + +export function createFilebeatConfig( + index: string, + results: FindFileStructureResponse, + ingestPipelineId: string, + username: string | null +) { + return [ + 'filebeat.inputs:', + '- type: log', + ...getPaths(), + ...getEncoding(results), + ...getExcludeLines(results), + ...getMultiline(results), + '', + ...getProcessors(results), + 'output.elasticsearch:', + ' hosts: [""]', + ...getUserDetails(username), + ` index: "${index}"`, + ` pipeline: "${ingestPipelineId}"`, + '', + 'setup:', + ' template.enabled: false', + ' ilm.enabled: false', + ].join('\n'); +} + +function getPaths() { + const txt = i18n.translate('xpack.ml.fileDatavisualizer.fileBeatConfig.paths', { + defaultMessage: 'add path to your files here', + }); + return [' paths:', ` - '<${txt}>'`]; +} + +function getEncoding(results: any) { + return results.charset !== 'UTF-8' ? [` encoding: ${results.charset}`] : []; +} + +function getExcludeLines(results: any) { + return results.exclude_lines_pattern !== undefined + ? [` exclude_lines: ['${results.exclude_lines_pattern.replace(/'/g, "''")}']`] + : []; +} + +function getMultiline(results: any) { + return results.multiline_start_pattern !== undefined + ? [ + ' multiline:', + ` pattern: '${results.multiline_start_pattern.replace(/'/g, "''")}'`, + ' match: after', + ' negate: true', + ] + : []; +} + +function getProcessors(results: any) { + return results.need_client_timezone === true ? ['processors:', '- add_locale: ~', ''] : []; +} + +function getUserDetails(username: string | null) { + return username !== null ? [` username: "${username}"`, ' password: ""'] : []; +} diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx new file mode 100644 index 00000000000000..30fc74acbabf49 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx @@ -0,0 +1,162 @@ +/* + * 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. + */ +/* + * 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, { FC, useState, useEffect } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiFlyout, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiButtonEmpty, + EuiTitle, + EuiFlyoutBody, + EuiSpacer, + EuiCodeBlock, + EuiCode, + EuiCopy, +} from '@elastic/eui'; +import { createFilebeatConfig } from './filebeat_config'; +import { useMlKibana } from '../../../../contexts/kibana'; +import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; + +export enum EDITOR_MODE { + HIDDEN, + READONLY, + EDITABLE, +} +interface Props { + index: string; + results: FindFileStructureResponse; + indexPatternId: string; + ingestPipelineId: string; + closeFlyout(): void; +} +export const FilebeatConfigFlyout: FC = ({ + index, + results, + indexPatternId, + ingestPipelineId, + closeFlyout, +}) => { + const [fileBeatConfig, setFileBeatConfig] = useState(''); + const [username, setUsername] = useState(null); + const { + services: { security }, + } = useMlKibana(); + + useEffect(() => { + security.authc.getCurrentUser().then(user => { + setUsername(user.username === undefined ? null : user.username); + }); + }, []); + + useEffect(() => { + const config = createFilebeatConfig(index, results, ingestPipelineId, username); + setFileBeatConfig(config); + }, [username]); + + return ( + + + + + + + + + + + + + + + + {copy => ( + + + + )} + + + + + + ); +}; + +const Contents: FC<{ + value: string; + index: string; + username: string | null; +}> = ({ value, index, username }) => { + return ( + + +
+ +
+
+ +

+ {index} }} + /> +

+

+ filebeat.yml }} + /> +

+ + + + {value} + + +

+ {username === null ? ( + {''}, + }} + /> + ) : ( + {username}, + password: {''}, + esUrl: {''}, + }} + /> + )} +

+
+ ); +}; diff --git a/x-pack/legacy/plugins/siem/server/graphql/alerts/index.ts b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/index.ts similarity index 71% rename from x-pack/legacy/plugins/siem/server/graphql/alerts/index.ts rename to x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/index.ts index f2beae525ed6b7..9286b92c2ab97c 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/alerts/index.ts +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { createAlertsResolvers } from './resolvers'; -export { alertsSchema } from './schema.gql'; +export { FilebeatConfigFlyout } from './filebeat_config_flyout'; diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js index beb5918e277aed..bdfc27099a1853 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { importerFactory } from './importer'; import { ResultsLinks } from '../results_links'; +import { FilebeatConfigFlyout } from '../filebeat_config_flyout'; import { ImportProgress, IMPORT_STATUS } from '../import_progress'; import { ImportErrors } from '../import_errors'; import { ImportSummary } from '../import_summary'; @@ -64,6 +65,7 @@ const DEFAULT_STATE = { indexNameError: '', indexPatternNameError: '', timeFieldName: undefined, + isFilebeatFlyoutVisible: false, }; export class ImportView extends Component { @@ -384,6 +386,16 @@ export class ImportView extends Component { }); }; + showFilebeatFlyout = () => { + this.setState({ isFilebeatFlyoutVisible: true }); + this.props.hideBottomBar(); + }; + + closeFilebeatFlyout = () => { + this.setState({ isFilebeatFlyoutVisible: false }); + this.props.showBottomBar(); + }; + async loadIndexNames() { const indices = await ml.getIndices(); const indexNames = indices.map(i => i.name); @@ -424,6 +436,7 @@ export class ImportView extends Component { indexNameError, indexPatternNameError, timeFieldName, + isFilebeatFlyoutVisible, } = this.state; const createPipeline = pipelineString !== ''; @@ -549,7 +562,18 @@ export class ImportView extends Component { indexPatternId={indexPatternId} timeFieldName={timeFieldName} createIndexPattern={createIndexPattern} + showFilebeatFlyout={this.showFilebeatFlyout} /> + + {isFilebeatFlyoutVisible && ( + + )} )} diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js index 710fa49e641679..27899a58beed2f 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js @@ -148,17 +148,17 @@ function populateFailures(error, failures, chunkCount) { } } -// The file structure endpoint sets the timezone to be {{ beat.timezone }} +// The file structure endpoint sets the timezone to be {{ event.timezone }} // as that's the variable Filebeat would send the client timezone in. // In this data import function the UI is effectively performing the role of Filebeat, // i.e. doing basic parsing, processing and conversion to JSON before forwarding to the ingest pipeline. // But it's not sending every single field that Filebeat would add, so the ingest pipeline -// cannot look for a beat.timezone variable in each input record. -// Therefore we need to replace {{ beat.timezone }} with the actual browser timezone +// cannot look for a event.timezone variable in each input record. +// Therefore we need to replace {{ event.timezone }} with the actual browser timezone function updatePipelineTimezone(ingestPipeline) { if (ingestPipeline !== undefined && ingestPipeline.processors && ingestPipeline.processors) { const dateProcessor = ingestPipeline.processors.find( - p => p.date !== undefined && p.date.timezone === '{{ beat.timezone }}' + p => p.date !== undefined && p.date.timezone === '{{ event.timezone }}' ); if (dateProcessor) { diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js index 840248817945a2..c2d3ac69f0963f 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js @@ -77,6 +77,11 @@ export class MessageImporter extends Importer { if (this.multilineStartRegex === null || line.match(this.multilineStartRegex) !== null) { this.addMessage(data, message); message = ''; + } else if (data.length === 0) { + // discard everything before the first line that is considered the first line of a message + // as it could be left over partial data from a spilt or rolled over log, + // or could be a blank line after the header in a csv file + return ''; } else { message += '\n'; } diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.ts similarity index 100% rename from x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.js rename to x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.ts diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.js deleted file mode 100644 index aaebca2f58963a..00000000000000 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.js +++ /dev/null @@ -1,182 +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 { FormattedMessage } from '@kbn/i18n/react'; -import React, { Component } from 'react'; - -import { EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; - -import moment from 'moment'; - -import { ml } from '../../../../services/ml_api_service'; -import { isFullLicense } from '../../../../license/check_license'; -import { checkPermission } from '../../../../privilege/check_privilege'; -import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes'; -import { withKibana } from '../../../../../../../../../../src/plugins/kibana_react/public'; - -const RECHECK_DELAY_MS = 3000; - -class ResultsLinksUI extends Component { - constructor(props) { - super(props); - - this.state = { - from: 'now-30m', - to: 'now', - }; - - this.recheckTimeout = null; - this.showCreateJobLink = true; - } - - componentDidMount() { - this.showCreateJobLink = checkPermission('canCreateJob') && mlNodesAvailable(); - // if this data has a time field, - // find the start and end times - if (this.props.timeFieldName !== undefined) { - this.updateTimeValues(); - } - } - - componentWillUnmount() { - clearTimeout(this.recheckTimeout); - } - - async updateTimeValues(recheck = true) { - const { index, timeFieldName } = this.props; - - const { from, to } = await getFullTimeRange(index, timeFieldName); - this.setState({ - from: from === null ? this.state.from : from, - to: to === null ? this.state.to : to, - }); - - // these links may have been drawn too quickly for the index to be ready - // to give us the correct start and end times. - // especially if the data was small. - // so if the start and end were null, try again in 3s - // the timeout is cleared when this component unmounts. just in case the user - // resets the form or navigates away within 3s - if (recheck && (from === null || to === null)) { - this.recheckTimeout = setTimeout(() => { - this.updateTimeValues(false); - }, RECHECK_DELAY_MS); - } - } - - render() { - const { index, indexPatternId, timeFieldName, createIndexPattern } = this.props; - - const { from, to } = this.state; - - const _g = - this.props.timeFieldName !== undefined - ? `&_g=(time:(from:'${from}',mode:quick,to:'${to}'))` - : ''; - - const { basePath } = this.props.kibana.services.http; - return ( - - {createIndexPattern && ( - - } - title={ - - } - description="" - href={`${basePath.get()}/app/kibana#/discover?&_a=(index:'${indexPatternId}')${_g}`} - /> - - )} - - {isFullLicense() === true && - timeFieldName !== undefined && - this.showCreateJobLink && - createIndexPattern && ( - - } - title={ - - } - description="" - href={`#/jobs/new_job/step/job_type?index=${indexPatternId}${_g}`} - /> - - )} - - {createIndexPattern && ( - - } - title={ - - } - description="" - href={`#/jobs/new_job/datavisualizer?index=${indexPatternId}${_g}`} - /> - - )} - - - } - title={ - - } - description="" - href={`${basePath.get()}/app/kibana#/management/elasticsearch/index_management/indices/filter/${index}`} - /> - - - - } - title={ - - } - description="" - href={`${basePath.get()}/app/kibana#/management/kibana/index_patterns/${ - createIndexPattern ? indexPatternId : '' - }`} - /> - - - ); - } -} - -export const ResultsLinks = withKibana(ResultsLinksUI); - -async function getFullTimeRange(index, timeFieldName) { - const query = { bool: { must: [{ query_string: { analyze_wildcard: true, query: '*' } }] } }; - const resp = await ml.getTimeFieldRange({ - index, - timeFieldName, - query, - }); - - return { - from: moment(resp.start.epoch).toISOString(), - to: moment(resp.end.epoch).toISOString(), - }; -} diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx new file mode 100644 index 00000000000000..debadba19051b7 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx @@ -0,0 +1,190 @@ +/* + * 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, { FC, useState, useEffect } from 'react'; +import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; +import { ml } from '../../../../services/ml_api_service'; +import { isFullLicense } from '../../../../license/check_license'; +import { checkPermission } from '../../../../privilege/check_privilege'; +import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes'; +import { useMlKibana } from '../../../../contexts/kibana'; + +const RECHECK_DELAY_MS = 3000; + +interface Props { + index: string; + indexPatternId: string; + timeFieldName?: string; + createIndexPattern: boolean; + showFilebeatFlyout(): void; +} + +export const ResultsLinks: FC = ({ + index, + indexPatternId, + timeFieldName, + createIndexPattern, + showFilebeatFlyout, +}) => { + const [duration, setDuration] = useState({ + from: 'now-30m', + to: 'now', + }); + const [showCreateJobLink, setShowCreateJobLink] = useState(false); + const [globalStateString, setGlobalStateString] = useState(''); + const { + services: { + http: { basePath }, + }, + } = useMlKibana(); + + useEffect(() => { + setShowCreateJobLink(checkPermission('canCreateJob') && mlNodesAvailable()); + updateTimeValues(); + }, []); + + useEffect(() => { + const _g = + timeFieldName !== undefined + ? `&_g=(time:(from:'${duration.from}',mode:quick,to:'${duration.to}'))` + : ''; + setGlobalStateString(_g); + }, [duration]); + + async function updateTimeValues(recheck = true) { + if (timeFieldName !== undefined) { + const { from, to } = await getFullTimeRange(index, timeFieldName); + setDuration({ + from: from === null ? duration.from : from, + to: to === null ? duration.to : to, + }); + + // these links may have been drawn too quickly for the index to be ready + // to give us the correct start and end times. + // especially if the data was small. + // so if the start and end were null, try again in 3s + if (recheck && (from === null || to === null)) { + setTimeout(() => { + updateTimeValues(false); + }, RECHECK_DELAY_MS); + } + } + } + + return ( + + {createIndexPattern && ( + + } + title={ + + } + description="" + href={`${basePath.get()}/app/kibana#/discover?&_a=(index:'${indexPatternId}')${globalStateString}`} + /> + + )} + + {isFullLicense() === true && + timeFieldName !== undefined && + showCreateJobLink && + createIndexPattern && ( + + } + title={ + + } + description="" + href={`#/jobs/new_job/step/job_type?index=${indexPatternId}${globalStateString}`} + /> + + )} + + {createIndexPattern && ( + + } + title={ + + } + description="" + href={`#/jobs/new_job/datavisualizer?index=${indexPatternId}${globalStateString}`} + /> + + )} + + + } + title={ + + } + description="" + href={`${basePath.get()}/app/kibana#/management/elasticsearch/index_management/indices/filter/${index}`} + /> + + + + } + title={ + + } + description="" + href={`${basePath.get()}/app/kibana#/management/kibana/index_patterns/${ + createIndexPattern ? indexPatternId : '' + }`} + /> + + + } + title={ + + } + description="" + onClick={showFilebeatFlyout} + /> + + + ); +}; + +async function getFullTimeRange(index: string, timeFieldName: string) { + const query = { bool: { must: [{ query_string: { analyze_wildcard: true, query: '*' } }] } }; + const resp = await ml.getTimeFieldRange({ + index, + timeFieldName, + query, + }); + + return { + from: moment(resp.start.epoch).toISOString(), + to: moment(resp.end.epoch).toISOString(), + }; +} diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/calendars/calendars_selection.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/calendars/calendars_selection.tsx index 919972186761ac..1e7327552623e5 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/calendars/calendars_selection.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/calendars/calendars_selection.tsx @@ -23,6 +23,7 @@ import { JobCreatorContext } from '../../../../../job_creator_context'; import { Description } from './description'; import { ml } from '../../../../../../../../../services/ml_api_service'; import { Calendar } from '../../../../../../../../../../../common/types/calendars'; +import { GLOBAL_CALENDAR } from '../../../../../../../../../../../common/constants/calendars'; export const CalendarsSelection: FC = () => { const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext); @@ -35,7 +36,9 @@ export const CalendarsSelection: FC = () => { async function loadCalendars() { setIsLoading(true); - const calendars = await ml.calendars(); + const calendars = (await ml.calendars()).filter( + c => c.job_ids.includes(GLOBAL_CALENDAR) === false + ); setOptions(calendars.map(c => ({ label: c.calendar_id, value: c }))); setSelectedOptions(selectedCalendars.map(c => ({ label: c.calendar_id, value: c }))); setIsLoading(false); diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/__snapshots__/new_calendar.test.js.snap b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/__snapshots__/new_calendar.test.js.snap index 2f5eb596a157b4..21f505cff9aec6 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/__snapshots__/new_calendar.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/__snapshots__/new_calendar.test.js.snap @@ -22,6 +22,7 @@ exports[`NewCalendar Renders new calendar form 1`] = ` eventsList={Array []} groupIds={Array []} isEdit={false} + isGlobalCalendar={false} isNewCalendarIdValid={true} jobIds={Array []} onCalendarIdChange={[Function]} @@ -30,6 +31,7 @@ exports[`NewCalendar Renders new calendar form 1`] = ` onDescriptionChange={[Function]} onEdit={[Function]} onEventDelete={[Function]} + onGlobalCalendarChange={[Function]} onGroupSelection={[Function]} onJobSelection={[Function]} saving={false} diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap index 0e7db62e44b51b..acce01f1994db2 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap @@ -84,59 +84,19 @@ exports[`CalendarForm Renders calendar form 1`] = ` value="" /> - - } - labelType="label" - > - - - + } - labelType="label" - > - - + name="switch" + /> diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/calendar_form.js b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/calendar_form.js index fffcdf4c516f8a..62daced72ceb25 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/calendar_form.js +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/calendar_form/calendar_form.js @@ -18,6 +18,7 @@ import { EuiSpacer, EuiText, EuiTitle, + EuiSwitch, } from '@elastic/eui'; import { EventsTable } from '../events_table'; @@ -68,6 +69,8 @@ export const CalendarForm = ({ selectedGroupOptions, selectedJobOptions, showNewEventModal, + isGlobalCalendar, + onGlobalCalendarChange, }) => { const msg = i18n.translate('xpack.ml.calendarsEdit.calendarForm.allowedCharactersDescription', { defaultMessage: @@ -81,7 +84,9 @@ export const CalendarForm = ({ return ( - {!isEdit && ( + {isEdit === true ? ( + + ) : (

@@ -128,39 +133,59 @@ export const CalendarForm = ({ )} - {isEdit && } - - } - > - - - + + } - > - - + checked={isGlobalCalendar} + onChange={onGlobalCalendarChange} + /> + + {isGlobalCalendar === false && ( + <> + + + + } + > + + + + + } + > + + + + )} @@ -240,4 +265,6 @@ CalendarForm.propTypes = { selectedGroupOptions: PropTypes.array.isRequired, selectedJobOptions: PropTypes.array.isRequired, showNewEventModal: PropTypes.func.isRequired, + isGlobalCalendar: PropTypes.bool.isRequired, + onGlobalCalendarChange: PropTypes.func.isRequired, }; diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js index 935e67ec05eff1..815d1565d5bc45 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js @@ -19,6 +19,7 @@ import { NewEventModal } from './new_event_modal'; import { ImportModal } from './import_modal'; import { ml } from '../../../services/ml_api_service'; import { withKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { GLOBAL_CALENDAR } from '../../../../../common/constants/calendars'; class NewCalendarUI extends Component { static propTypes = { @@ -46,6 +47,7 @@ class NewCalendarUI extends Component { events: [], saving: false, selectedCalendar: undefined, + isGlobalCalendar: false, }; } @@ -65,6 +67,7 @@ class NewCalendarUI extends Component { let eventsList = []; let selectedCalendar; let formCalendarId = ''; + let isGlobalCalendar = false; // Editing existing calendar. if (this.props.calendarId !== undefined) { @@ -74,13 +77,17 @@ class NewCalendarUI extends Component { formCalendarId = selectedCalendar.calendar_id; eventsList = selectedCalendar.events; - selectedCalendar.job_ids.forEach(id => { - if (jobIds.find(jobId => jobId === id)) { - selectedJobOptions.push({ label: id }); - } else if (groupIds.find(groupId => groupId === id)) { - selectedGroupOptions.push({ label: id }); - } - }); + if (selectedCalendar.job_ids.includes(GLOBAL_CALENDAR)) { + isGlobalCalendar = true; + } else { + selectedCalendar.job_ids.forEach(id => { + if (jobIds.find(jobId => jobId === id)) { + selectedJobOptions.push({ label: id }); + } else if (groupIds.find(groupId => groupId === id)) { + selectedGroupOptions.push({ label: id }); + } + }); + } } } @@ -96,6 +103,7 @@ class NewCalendarUI extends Component { selectedJobOptions, selectedGroupOptions, selectedCalendar, + isGlobalCalendar, }); } catch (error) { console.log(error); @@ -181,10 +189,15 @@ class NewCalendarUI extends Component { events, selectedGroupOptions, selectedJobOptions, + isGlobalCalendar, } = this.state; - const jobIds = selectedJobOptions.map(option => option.label); - const groupIds = selectedGroupOptions.map(option => option.label); + const allIds = isGlobalCalendar + ? [GLOBAL_CALENDAR] + : [ + ...selectedJobOptions.map(option => option.label), + ...selectedGroupOptions.map(option => option.label), + ]; // Reduce events to fields expected by api const eventsToSave = events.map(event => ({ @@ -198,7 +211,7 @@ class NewCalendarUI extends Component { calendarId: formCalendarId, description, events: eventsToSave, - job_ids: [...jobIds, ...groupIds], + job_ids: allIds, }; return calendar; @@ -214,6 +227,12 @@ class NewCalendarUI extends Component { })); }; + onGlobalCalendarChange = ({ currentTarget }) => { + this.setState({ + isGlobalCalendar: currentTarget.checked, + }); + }; + onJobSelection = selectedJobOptions => { this.setState({ selectedJobOptions, @@ -295,6 +314,7 @@ class NewCalendarUI extends Component { selectedCalendar, selectedJobOptions, selectedGroupOptions, + isGlobalCalendar, } = this.state; let modal = ''; @@ -351,6 +371,8 @@ class NewCalendarUI extends Component { selectedJobOptions={selectedJobOptions} onCreateGroupOption={this.onCreateGroupOption} showNewEventModal={this.showNewEventModal} + isGlobalCalendar={isGlobalCalendar} + onGlobalCalendarChange={this.onGlobalCalendarChange} /> {modal} diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/utils.js b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/utils.js index e4ab6677accf5b..efc54c181fdc1c 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/utils.js +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/utils.js @@ -73,14 +73,17 @@ function getCalendars() { export function getCalendarSettingsData() { return new Promise(async (resolve, reject) => { try { - const data = await Promise.all([getJobIds(), getGroupIds(), getCalendars()]); + const [jobIds, groupIds, calendars] = await Promise.all([ + getJobIds(), + getGroupIds(), + getCalendars(), + ]); - const formattedData = { - jobIds: data[0], - groupIds: data[1], - calendars: data[2], - }; - resolve(formattedData); + resolve({ + jobIds, + groupIds, + calendars, + }); } catch (error) { console.log(error); reject(error); diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap b/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap index ff74c592b2b0f6..14b65a04ce599d 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap @@ -16,6 +16,7 @@ exports[`CalendarsListTable renders the table with all calendars 1`] = ` Object { "field": "job_ids_string", "name": "Jobs", + "render": [Function], "sortable": true, "truncateText": true, }, diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/table.js b/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/table.js index bd1dafcd6c0aaf..be41eabd5ae2d1 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/table.js +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/table.js @@ -12,6 +12,8 @@ import { EuiButton, EuiLink, EuiInMemoryTable } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { GLOBAL_CALENDAR } from '../../../../../../common/constants/calendars'; + export const CalendarsListTable = ({ calendarsList, onDeleteClick, @@ -52,6 +54,18 @@ export const CalendarsListTable = ({ }), sortable: true, truncateText: true, + render: jobList => { + return jobList === GLOBAL_CALENDAR ? ( + + + + ) : ( + jobList + ); + }, }, { field: 'events_length', diff --git a/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts index 8857485a586441..f837d90dba8fe9 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts @@ -20,6 +20,7 @@ import { ChromeRecentlyAccessed, IBasePath, } from 'kibana/public'; +import { SecurityPluginSetup } from '../../../../../../plugins/security/public'; export interface DependencyCache { timefilter: TimefilterSetup | null; @@ -38,6 +39,7 @@ export interface DependencyCache { APP_URL: string | null; application: ApplicationStart | null; http: HttpStart | null; + security: SecurityPluginSetup | null; } const cache: DependencyCache = { @@ -57,6 +59,7 @@ const cache: DependencyCache = { APP_URL: null, application: null, http: null, + security: null, }; export function setDependencyCache(deps: Partial) { @@ -189,6 +192,13 @@ export function getHttp() { return cache.http; } +export function getSecurity() { + if (cache.security === null) { + throw new Error("security hasn't been initialized"); + } + return cache.security; +} + export function clearCache() { console.log('clearing dependency cache'); // eslint-disable-line no-console Object.keys(cache).forEach(k => { diff --git a/x-pack/legacy/plugins/ml/public/legacy.ts b/x-pack/legacy/plugins/ml/public/legacy.ts index bf431f0986d68a..40a1afa06b5a64 100644 --- a/x-pack/legacy/plugins/ml/public/legacy.ts +++ b/x-pack/legacy/plugins/ml/public/legacy.ts @@ -6,14 +6,16 @@ import chrome from 'ui/chrome'; import { npSetup, npStart } from 'ui/new_platform'; - import { PluginInitializerContext } from 'src/core/public'; +import { SecurityPluginSetup } from '../../../../plugins/security/public'; + import { plugin } from '.'; const pluginInstance = plugin({} as PluginInitializerContext); export const setup = pluginInstance.setup(npSetup.core, { data: npStart.plugins.data, + security: ((npSetup.plugins as unknown) as { security: SecurityPluginSetup }).security, // security isn't in the PluginsSetup interface, but does exist __LEGACY: { XSRF: chrome.getXsrfToken(), // @ts-ignore getAppUrl is missing from chrome's definition diff --git a/x-pack/legacy/plugins/ml/public/plugin.ts b/x-pack/legacy/plugins/ml/public/plugin.ts index 79af300bce4ec0..cb39b31a32b148 100644 --- a/x-pack/legacy/plugins/ml/public/plugin.ts +++ b/x-pack/legacy/plugins/ml/public/plugin.ts @@ -8,7 +8,7 @@ import { Plugin, CoreStart, CoreSetup } from 'src/core/public'; import { MlDependencies } from './application/app'; export class MlPlugin implements Plugin { - setup(core: CoreSetup, { data, __LEGACY }: MlDependencies) { + setup(core: CoreSetup, { data, security, __LEGACY }: MlDependencies) { core.application.register({ id: 'ml', title: 'Machine learning', @@ -21,6 +21,7 @@ export class MlPlugin implements Plugin { onAppLeave: params.onAppLeave, data, __LEGACY, + security, }); }, }); diff --git a/x-pack/legacy/plugins/ml/server/models/calendar/event_manager.ts b/x-pack/legacy/plugins/ml/server/models/calendar/event_manager.ts index 19f2eda4661796..488839f68b3fe2 100644 --- a/x-pack/legacy/plugins/ml/server/models/calendar/event_manager.ts +++ b/x-pack/legacy/plugins/ml/server/models/calendar/event_manager.ts @@ -6,6 +6,8 @@ import Boom from 'boom'; +import { GLOBAL_CALENDAR } from '../../../common/constants/calendars'; + export interface CalendarEvent { calendar_id?: string; event_id?: string; @@ -32,7 +34,7 @@ export class EventManager { // jobId is optional async getAllEvents(jobId?: string) { - const calendarId = '_all'; + const calendarId = GLOBAL_CALENDAR; try { const resp = await this._client('ml.events', { calendarId, diff --git a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts index fd5b5221393fc9..9f30f609c60b63 100644 --- a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts +++ b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts @@ -6,6 +6,7 @@ import Boom from 'boom'; import { RequestHandlerContext } from 'kibana/server'; +import { FindFileStructureResponse } from '../../../common/types/file_datavisualizer'; export type InputData = any[]; @@ -20,31 +21,7 @@ export type FormattedOverrides = InputOverrides & { }; export interface AnalysisResult { - results: { - charset: string; - has_header_row: boolean; - has_byte_order_marker: boolean; - format: string; - field_stats: { - [fieldName: string]: { - count: number; - cardinality: number; - top_hits: Array<{ count: number; value: any }>; - }; - }; - sample_start: string; - num_messages_analyzed: number; - mappings: { - [fieldName: string]: { - type: string; - }; - }; - quote: string; - delimiter: string; - need_client_timezone: boolean; - num_lines_analyzed: number; - column_names: string[]; - }; + results: FindFileStructureResponse; overrides?: FormattedOverrides; } diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/groups.js b/x-pack/legacy/plugins/ml/server/models/job_service/groups.js index 91f82f04a9a0c4..6fbc071ef9854f 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_service/groups.js +++ b/x-pack/legacy/plugins/ml/server/models/job_service/groups.js @@ -5,6 +5,7 @@ */ import { CalendarManager } from '../calendar'; +import { GLOBAL_CALENDAR } from '../../../common/constants/calendars'; export function groupsProvider(callWithRequest) { const calMngr = new CalendarManager(callWithRequest); @@ -12,11 +13,13 @@ export function groupsProvider(callWithRequest) { async function getAllGroups() { const groups = {}; const jobIds = {}; - const [JOBS, CALENDARS] = [0, 1]; - const results = await Promise.all([callWithRequest('ml.jobs'), calMngr.getAllCalendars()]); + const [{ jobs }, calendars] = await Promise.all([ + callWithRequest('ml.jobs'), + calMngr.getAllCalendars(), + ]); - if (results[JOBS] && results[JOBS].jobs) { - results[JOBS].jobs.forEach(job => { + if (jobs) { + jobs.forEach(job => { jobIds[job.job_id] = null; if (job.groups !== undefined) { job.groups.forEach(g => { @@ -33,10 +36,11 @@ export function groupsProvider(callWithRequest) { } }); } - if (results[CALENDARS]) { - results[CALENDARS].forEach(cal => { + if (calendars) { + calendars.forEach(cal => { cal.job_ids.forEach(jId => { - if (jobIds[jId] === undefined) { + // don't include _all in the calendar groups list + if (jId !== GLOBAL_CALENDAR && jobIds[jId] === undefined) { if (groups[jId] === undefined) { groups[jId] = { id: jId, diff --git a/x-pack/legacy/plugins/remote_clusters/index.ts b/x-pack/legacy/plugins/remote_clusters/index.ts index 37b2224f8d7c25..439cb818d8a562 100644 --- a/x-pack/legacy/plugins/remote_clusters/index.ts +++ b/x-pack/legacy/plugins/remote_clusters/index.ts @@ -5,6 +5,8 @@ */ import { resolve } from 'path'; +import { Legacy } from 'kibana'; + import { PLUGIN } from './common'; export function remoteClusters(kibana: any) { @@ -28,7 +30,7 @@ export function remoteClusters(kibana: any) { enabled: Joi.boolean().default(true), }).default(); }, - isEnabled(config: any) { + isEnabled(config: Legacy.KibanaConfig) { return ( config.get('xpack.remote_clusters.enabled') && config.get('xpack.index_management.enabled') ); diff --git a/x-pack/legacy/plugins/rollup/index.ts b/x-pack/legacy/plugins/rollup/index.ts index 7548af23b3aae9..2c8363cc397f45 100644 --- a/x-pack/legacy/plugins/rollup/index.ts +++ b/x-pack/legacy/plugins/rollup/index.ts @@ -40,7 +40,7 @@ export function rollup(kibana: any) { }, init(server: any) { const { core: coreSetup, plugins } = server.newPlatform.setup; - const { usageCollection, metrics } = plugins; + const { usageCollection, metrics, indexManagement } = plugins; const rollupSetup = (plugins.rollup as unknown) as RollupSetup; @@ -54,11 +54,11 @@ export function rollup(kibana: any) { rollupPluginInstance.setup(coreSetup, { usageCollection, metrics, + indexManagement, __LEGACY: { plugins: { xpack_main: server.plugins.xpack_main, rollup: server.plugins[PLUGIN.ID], - index_management: server.plugins.index_management, }, }, }); diff --git a/x-pack/legacy/plugins/rollup/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts index 64eb1f64363892..e3e663ac7b0f44 100644 --- a/x-pack/legacy/plugins/rollup/public/legacy.ts +++ b/x-pack/legacy/plugins/rollup/public/legacy.ts @@ -10,7 +10,6 @@ import { aggTypeFieldFilters } from 'ui/agg_types'; import { addSearchStrategy } from '../../../../../src/plugins/data/public'; import { RollupPlugin } from './plugin'; import { setup as management } from '../../../../../src/legacy/core_plugins/management/public/legacy'; -import { extensionsService } from '../../index_management/public'; const plugin = new RollupPlugin(); @@ -20,7 +19,6 @@ export const setup = plugin.setup(npSetup.core, { aggTypeFilters, aggTypeFieldFilters, addSearchStrategy, - indexManagementExtensions: extensionsService, managementLegacy: management, }, }); diff --git a/x-pack/legacy/plugins/rollup/public/plugin.ts b/x-pack/legacy/plugins/rollup/public/plugin.ts index 90d7e2d9d01919..a01383f4733ef8 100644 --- a/x-pack/legacy/plugins/rollup/public/plugin.ts +++ b/x-pack/legacy/plugins/rollup/public/plugin.ts @@ -27,7 +27,7 @@ import { // @ts-ignore import { CRUD_APP_BASE_PATH } from './crud_app/constants'; import { ManagementSetup } from '../../../../../src/plugins/management/public'; -import { IndexMgmtSetup } from '../../index_management/public'; +import { IndexMgmtSetup } from '../../../../plugins/index_management/public'; // @ts-ignore import { setEsBaseAndXPackBase, setHttp } from './crud_app/services'; import { setNotifications, setFatalErrors } from './kibana_services'; @@ -39,30 +39,28 @@ export interface RollupPluginSetupDependencies { aggTypeFieldFilters: AggTypeFieldFilters; addSearchStrategy: (searchStrategy: SearchStrategyProvider) => void; managementLegacy: ManagementSetupLegacy; - indexManagementExtensions: IndexMgmtSetup['extensionsService']; }; home?: HomePublicPluginSetup; management: ManagementSetup; + indexManagement?: IndexMgmtSetup; } export class RollupPlugin implements Plugin { setup( core: CoreSetup, { - __LEGACY: { - aggTypeFilters, - aggTypeFieldFilters, - addSearchStrategy, - managementLegacy, - indexManagementExtensions, - }, + __LEGACY: { aggTypeFilters, aggTypeFieldFilters, addSearchStrategy, managementLegacy }, home, management, + indexManagement, }: RollupPluginSetupDependencies ) { setFatalErrors(core.fatalErrors); - indexManagementExtensions.addBadge(rollupBadgeExtension); - indexManagementExtensions.addToggle(rollupToggleExtension); + + if (indexManagement) { + indexManagement.extensionsService.addBadge(rollupBadgeExtension); + indexManagement.extensionsService.addToggle(rollupToggleExtension); + } const isRollupIndexPatternsEnabled = core.uiSettings.get(CONFIG_ROLLUPS); diff --git a/x-pack/legacy/plugins/rollup/server/plugin.ts b/x-pack/legacy/plugins/rollup/server/plugin.ts index 52b1e31af4eb22..090cb8a47377a1 100644 --- a/x-pack/legacy/plugins/rollup/server/plugin.ts +++ b/x-pack/legacy/plugins/rollup/server/plugin.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server'; +import { IndexMgmtSetup } from '../../../../plugins/index_management/server'; import { registerLicenseChecker } from '../../../server/lib/register_license_checker'; import { PLUGIN } from '../common'; import { ServerShim, RouteDependencies } from './types'; @@ -38,10 +39,12 @@ export class RollupsServerPlugin implements Plugin { __LEGACY: serverShim, usageCollection, metrics, + indexManagement, }: { __LEGACY: ServerShim; usageCollection?: UsageCollectionSetup; metrics?: VisTypeTimeseriesSetup; + indexManagement?: IndexMgmtSetup; } ) { const elasticsearch = await elasticsearchService.adminClient; @@ -76,11 +79,8 @@ export class RollupsServerPlugin implements Plugin { }); } - if ( - serverShim.plugins.index_management && - serverShim.plugins.index_management.addIndexManagementDataEnricher - ) { - serverShim.plugins.index_management.addIndexManagementDataEnricher(rollupDataEnricher); + if (indexManagement && indexManagement.indexDataEnricher) { + indexManagement.indexDataEnricher.add(rollupDataEnricher); } if (metrics) { diff --git a/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts index 7c5e160c54a31a..ad621f2d9ba806 100644 --- a/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts +++ b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -interface Index { - name: string; - [key: string]: unknown; -} +import { Index } from '../../../../plugins/index_management/server'; export const rollupDataEnricher = async (indicesList: Index[], callWithRequest: any) => { if (!indicesList || !indicesList.length) { - return indicesList; + return Promise.resolve(indicesList); } const params = { diff --git a/x-pack/legacy/plugins/rollup/server/types.ts b/x-pack/legacy/plugins/rollup/server/types.ts index 62a4841133cffb..bcc6770e9b8ee0 100644 --- a/x-pack/legacy/plugins/rollup/server/types.ts +++ b/x-pack/legacy/plugins/rollup/server/types.ts @@ -11,7 +11,6 @@ export interface ServerShim { plugins: { xpack_main: XPackMainPlugin; rollup: any; - index_management: any; }; } diff --git a/x-pack/legacy/plugins/siem/cypress/support/index.js b/x-pack/legacy/plugins/siem/cypress/support/index.js index 9b86c2ffa94c69..37fa920a8bc31a 100644 --- a/x-pack/legacy/plugins/siem/cypress/support/index.js +++ b/x-pack/legacy/plugins/siem/cypress/support/index.js @@ -26,5 +26,11 @@ Cypress.Cookies.defaults({ whitelist: 'sid', }); +Cypress.on('uncaught:exception', err => { + if (err.message.includes('ResizeObserver loop limit exceeded')) { + return false; + } +}); + // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/histogram_configs.ts b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/histogram_configs.ts new file mode 100644 index 00000000000000..fbcf4c6ed039b6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/histogram_configs.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import * as i18n from './translations'; +import { MatrixHistogramOption, MatrixHisrogramConfigs } from '../matrix_histogram/types'; +import { HistogramType } from '../../graphql/types'; + +export const alertsStackByOptions: MatrixHistogramOption[] = [ + { + text: 'event.category', + value: 'event.category', + }, + { + text: 'event.module', + value: 'event.module', + }, +]; + +const DEFAULT_STACK_BY = 'event.module'; + +export const histogramConfigs: MatrixHisrogramConfigs = { + defaultStackByOption: + alertsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[1], + errorMessage: i18n.ERROR_FETCHING_ALERTS_DATA, + histogramType: HistogramType.alerts, + stackByOptions: alertsStackByOptions, + subtitle: undefined, + title: i18n.ALERTS_GRAPH_TITLE, +}; diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx index a8c2f429040ea6..587002c24d5269 100644 --- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx @@ -3,30 +3,18 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { noop } from 'lodash/fp'; -import React, { useEffect, useCallback } from 'react'; +import React, { useEffect, useCallback, useMemo } from 'react'; import numeral from '@elastic/numeral'; import { AlertsComponentsQueryProps } from './types'; import { AlertsTable } from './alerts_table'; import * as i18n from './translations'; -import { MatrixHistogramOption } from '../matrix_histogram/types'; -import { MatrixHistogramContainer } from '../../containers/matrix_histogram'; -import { MatrixHistogramGqlQuery } from '../../containers/matrix_histogram/index.gql_query'; import { useUiSetting$ } from '../../lib/kibana'; import { DEFAULT_NUMBER_FORMAT } from '../../../common/constants'; +import { MatrixHistogramContainer } from '../matrix_histogram'; +import { histogramConfigs } from './histogram_configs'; +import { MatrixHisrogramConfigs } from '../matrix_histogram/types'; const ID = 'alertsOverTimeQuery'; -export const alertsStackByOptions: MatrixHistogramOption[] = [ - { - text: 'event.category', - value: 'event.category', - }, - { - text: 'event.module', - value: 'event.module', - }, -]; -const dataKey = 'AlertsHistogram'; export const AlertsView = ({ deleteQuery, @@ -34,21 +22,10 @@ export const AlertsView = ({ filterQuery, pageFilters, setQuery, - skip, startDate, type, - updateDateRange = noop, }: AlertsComponentsQueryProps) => { const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - - useEffect(() => { - return () => { - if (deleteQuery) { - deleteQuery({ id: ID }); - } - }; - }, []); - const getSubtitle = useCallback( (totalCount: number) => `${i18n.SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${i18n.UNIT( @@ -56,27 +33,32 @@ export const AlertsView = ({ )}`, [] ); + const alertsHistogramConfigs: MatrixHisrogramConfigs = useMemo( + () => ({ + ...histogramConfigs, + subtitle: getSubtitle, + }), + [getSubtitle] + ); + useEffect(() => { + return () => { + if (deleteQuery) { + deleteQuery({ id: ID }); + } + }; + }, [deleteQuery]); return ( <> diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/types.ts b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/types.ts index e6d6fdf273ec86..a24c66e31e6708 100644 --- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/types.ts @@ -13,14 +13,7 @@ type CommonQueryProps = HostsComponentsQueryProps | NetworkComponentQueryProps; export interface AlertsComponentsQueryProps extends Pick< CommonQueryProps, - | 'deleteQuery' - | 'endDate' - | 'filterQuery' - | 'skip' - | 'setQuery' - | 'startDate' - | 'type' - | 'updateDateRange' + 'deleteQuery' | 'endDate' | 'filterQuery' | 'skip' | 'setQuery' | 'startDate' | 'type' > { pageFilters: Filter[]; stackByOptions?: MatrixHistogramOption[]; diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx index 88c77755527618..7377bcbe7050fa 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx @@ -30,10 +30,10 @@ const chartDefaultRendering: Rendering = 'canvas'; export type UpdateDateRange = (min: number, max: number) => void; export interface ChartData { - x: number | string | null; - y: number | string | null; + x?: number | string | null; + y?: number | string | null; y0?: number; - g?: number | string; + g?: number | string | null; } export interface ChartSeriesConfigs { diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000000000..0e518e48e2e888 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Matrix Histogram Component not initial load it renders no MatrixLoader 1`] = `"
"`; + +exports[`Matrix Histogram Component on initial load it renders MatrixLoader 1`] = `"
"`; diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx index a44efed47372db..db5b1f7f03ee3d 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx @@ -6,17 +6,19 @@ /* eslint-disable react/display-name */ -import { shallow } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; import React from 'react'; import { MatrixHistogram } from '.'; -import { MatrixHistogramGqlQuery as mockQuery } from '../../containers/matrix_histogram/index.gql_query'; - +import { useQuery } from '../../containers/matrix_histogram'; +import { HistogramType } from '../../graphql/types'; jest.mock('../../lib/kibana'); -jest.mock('../loader', () => { +jest.mock('./matrix_loader', () => { return { - Loader: () =>
, + MatrixLoader: () => { + return
; + }, }; }); @@ -32,17 +34,31 @@ jest.mock('../charts/barchart', () => { }; }); +jest.mock('../../containers/matrix_histogram', () => { + return { + useQuery: jest.fn(), + }; +}); + +jest.mock('../../components/matrix_histogram/utils', () => { + return { + getBarchartConfigs: jest.fn(), + getCustomChartData: jest.fn().mockReturnValue(true), + }; +}); + describe('Matrix Histogram Component', () => { + let wrapper: ReactWrapper; + const mockMatrixOverTimeHistogramProps = { - dataKey: 'mockDataKey', defaultIndex: ['defaultIndex'], defaultStackByOption: { text: 'text', value: 'value' }, endDate: new Date('2019-07-18T20:00:00.000Z').valueOf(), errorMessage: 'error', + histogramType: HistogramType.alerts, id: 'mockId', isInspected: false, isPtrIncluded: false, - query: mockQuery, setQuery: jest.fn(), skip: false, sourceId: 'default', @@ -52,36 +68,56 @@ describe('Matrix Histogram Component', () => { subtitle: 'mockSubtitle', totalCount: -1, title: 'mockTitle', - updateDateRange: jest.fn(), + dispatchSetAbsoluteRangeDatePicker: jest.fn(), }; - describe('rendering', () => { - test('it renders EuiLoadingContent on initialLoad', () => { - const wrapper = shallow(); - expect(wrapper.find(`[data-test-subj="initialLoadingPanelMatrixOverTime"]`)).toBeTruthy(); + beforeAll(() => { + (useQuery as jest.Mock).mockReturnValue({ + data: null, + loading: false, + inspect: false, + totalCount: null, }); - - test('it renders Loader while fetching data if visited before', () => { - const mockProps = { - ...mockMatrixOverTimeHistogramProps, - data: [{ x: new Date('2019-09-16T02:20:00.000Z').valueOf(), y: 3787, g: 'config_change' }], - totalCount: 10, - loading: true, - }; - const wrapper = shallow(); - expect(wrapper.find('.loader')).toBeTruthy(); + wrapper = mount(); + }); + describe('on initial load', () => { + test('it renders MatrixLoader', () => { + expect(wrapper.html()).toMatchSnapshot(); + expect(wrapper.find('MatrixLoader').exists()).toBe(true); }); + }); - test('it renders BarChart if data available', () => { - const mockProps = { - ...mockMatrixOverTimeHistogramProps, - data: [{ x: new Date('2019-09-16T02:20:00.000Z').valueOf(), y: 3787, g: 'config_change' }], - totalCount: 10, + describe('not initial load', () => { + beforeAll(() => { + (useQuery as jest.Mock).mockReturnValue({ + data: [ + { x: 1, y: 2, g: 'g1' }, + { x: 2, y: 4, g: 'g1' }, + { x: 3, y: 6, g: 'g1' }, + { x: 1, y: 1, g: 'g2' }, + { x: 2, y: 3, g: 'g2' }, + { x: 3, y: 5, g: 'g2' }, + ], loading: false, - }; - const wrapper = shallow(); + inspect: false, + totalCount: 1, + }); + wrapper.setProps({ endDate: 100 }); + wrapper.update(); + }); + test('it renders no MatrixLoader', () => { + expect(wrapper.html()).toMatchSnapshot(); + expect(wrapper.find(`MatrixLoader`).exists()).toBe(false); + }); + + test('it shows BarChart if data available', () => { + expect(wrapper.find(`.barchart`).exists()).toBe(true); + }); + }); - expect(wrapper.find(`.barchart`)).toBeTruthy(); + describe('select dropdown', () => { + test('should be hidden if only one option is provided', () => { + expect(wrapper.find('EuiSelect').exists()).toBe(false); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx index 04b988f8270f37..cb9afde899cf80 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx @@ -4,19 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect, useCallback } from 'react'; -import { ScaleType } from '@elastic/charts'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import { Position } from '@elastic/charts'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiSelect, EuiSpacer } from '@elastic/eui'; import { noop } from 'lodash/fp'; +import { compose } from 'redux'; +import { connect } from 'react-redux'; import * as i18n from './translations'; import { BarChart } from '../charts/barchart'; import { HeaderSection } from '../header_section'; import { MatrixLoader } from './matrix_loader'; import { Panel } from '../panel'; -import { getBarchartConfigs, getCustomChartData } from './utils'; -import { useQuery } from '../../containers/matrix_histogram/utils'; +import { getBarchartConfigs, getCustomChartData } from '../../components/matrix_histogram/utils'; +import { useQuery } from '../../containers/matrix_histogram'; import { MatrixHistogramProps, MatrixHistogramOption, @@ -26,6 +28,35 @@ import { import { ChartSeriesData } from '../charts/common'; import { InspectButtonContainer } from '../inspect'; +import { State, inputsSelectors, hostsModel, networkModel } from '../../store'; + +import { + MatrixHistogramMappingTypes, + GetTitle, + GetSubTitle, +} from '../../components/matrix_histogram/types'; +import { SetQuery } from '../../pages/hosts/navigation/types'; +import { QueryTemplateProps } from '../../containers/query_template'; +import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions'; +import { HistogramType } from '../../graphql/types'; + +export interface OwnProps extends QueryTemplateProps { + defaultStackByOption: MatrixHistogramOption; + errorMessage: string; + headerChildren?: React.ReactNode; + hideHistogramIfEmpty?: boolean; + histogramType: HistogramType; + id: string; + legendPosition?: Position; + mapping?: MatrixHistogramMappingTypes; + setQuery: SetQuery; + showLegend?: boolean; + stackByOptions: MatrixHistogramOption[]; + subtitle?: string | GetSubTitle; + title: string | GetTitle; + type: hostsModel.HostsType | networkModel.NetworkType; +} + const DEFAULT_PANEL_HEIGHT = 300; const HeaderChildrenFlexItem = styled(EuiFlexItem)` @@ -41,45 +72,50 @@ const HistogramPanel = styled(Panel)<{ height?: number }>` export const MatrixHistogramComponent: React.FC = ({ chartHeight, - dataKey, defaultStackByOption, endDate, errorMessage, filterQuery, headerChildren, + histogramType, hideHistogramIfEmpty = false, id, - isAlertsHistogram, - isAnomaliesHistogram, - isAuthenticationsHistogram, - isDnsHistogram, - isEventsHistogram, isInspected, - legendPosition = 'right', + legendPosition, mapping, panelHeight = DEFAULT_PANEL_HEIGHT, - query, - scaleType = ScaleType.Time, setQuery, - showLegend = true, - skip, + showLegend, stackByOptions, startDate, subtitle, title, - updateDateRange, + dispatchSetAbsoluteRangeDatePicker, yTickFormatter, }) => { - const barchartConfigs = getBarchartConfigs({ - chartHeight, - from: startDate, - legendPosition, - to: endDate, - onBrushEnd: updateDateRange, - scaleType, - yTickFormatter, - showLegend, - }); + const barchartConfigs = useMemo( + () => + getBarchartConfigs({ + chartHeight, + from: startDate, + legendPosition, + to: endDate, + onBrushEnd: (min: number, max: number) => { + dispatchSetAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + yTickFormatter, + showLegend, + }), + [ + chartHeight, + startDate, + legendPosition, + endDate, + dispatchSetAbsoluteRangeDatePicker, + yTickFormatter, + showLegend, + ] + ); const [isInitialLoading, setIsInitialLoading] = useState(true); const [selectedStackByOption, setSelectedStackByOption] = useState( defaultStackByOption @@ -100,19 +136,11 @@ export const MatrixHistogramComponent: React.FC( { - dataKey, endDate, errorMessage, filterQuery, - query, - skip, + histogramType, startDate, - title, - isAlertsHistogram, - isAnomaliesHistogram, - isAuthenticationsHistogram, - isDnsHistogram, - isEventsHistogram, isInspected, stackByField: selectedStackByOption.value, } @@ -129,7 +157,6 @@ export const MatrixHistogramComponent: React.FC= 0 ? subtitleWithCounts : null)} + subtitle={!isInitialLoading && (totalCount >= 0 ? subtitleWithCounts : null)} > @@ -197,7 +227,10 @@ export const MatrixHistogramComponent: React.FC= 0 ? subtitleWithCounts : null)} + subtitle={ + !isInitialLoading && + (totalCount != null && totalCount >= 0 ? subtitleWithCounts : null) + } > @@ -224,3 +257,20 @@ export const MatrixHistogramComponent: React.FC { + const getQuery = inputsSelectors.globalQueryByIdSelector(); + const mapStateToProps = (state: State, { type, id }: OwnProps) => { + const { isInspected } = getQuery(state, id); + return { + isInspected, + }; + }; + return mapStateToProps; +}; + +export const MatrixHistogramContainer = compose>( + connect(makeMapStateToProps, { + dispatchSetAbsoluteRangeDatePicker: setAbsoluteRangeDatePicker, + }) +)(MatrixHistogram); diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts index 88f8f1ff28fa92..fda4f5d15d95c7 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts @@ -4,20 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ScaleType, Position } from '@elastic/charts'; -import { SetStateAction } from 'react'; -import { DocumentNode } from 'graphql'; -import { - MatrixOverTimeHistogramData, - MatrixOverOrdinalHistogramData, - NetworkDnsSortField, - PaginationInputPaginated, -} from '../../graphql/types'; -import { UpdateDateRange } from '../charts/common'; +import { ScaleType, Position, TickFormatter } from '@elastic/charts'; +import { ActionCreator } from 'redux'; import { ESQuery } from '../../../common/typed_json'; import { SetQuery } from '../../pages/hosts/navigation/types'; +import { InputsModelId } from '../../store/inputs/constants'; +import { HistogramType } from '../../graphql/types'; +import { UpdateDateRange } from '../charts/common'; -export type MatrixHistogramDataTypes = MatrixOverTimeHistogramData | MatrixOverOrdinalHistogramData; export type MatrixHistogramMappingTypes = Record< string, { key: string; value: null; color?: string | undefined } @@ -30,10 +24,27 @@ export interface MatrixHistogramOption { export type GetSubTitle = (count: number) => string; export type GetTitle = (matrixHistogramOption: MatrixHistogramOption) => string; -export interface MatrixHistogramBasicProps { +export interface MatrixHisrogramConfigs { + defaultStackByOption: MatrixHistogramOption; + errorMessage: string; + hideHistogramIfEmpty?: boolean; + histogramType: HistogramType; + legendPosition?: Position; + mapping?: MatrixHistogramMappingTypes; + stackByOptions: MatrixHistogramOption[]; + subtitle?: string | GetSubTitle; + title: string | GetTitle; +} + +interface MatrixHistogramBasicProps { chartHeight?: number; defaultIndex: string[]; defaultStackByOption: MatrixHistogramOption; + dispatchSetAbsoluteRangeDatePicker: ActionCreator<{ + id: InputsModelId; + from: number; + to: number; + }>; endDate: number; headerChildren?: React.ReactNode; hideHistogramIfEmpty?: boolean; @@ -42,35 +53,20 @@ export interface MatrixHistogramBasicProps { mapping?: MatrixHistogramMappingTypes; panelHeight?: number; setQuery: SetQuery; - sourceId: string; startDate: number; stackByOptions: MatrixHistogramOption[]; subtitle?: string | GetSubTitle; - title?: string; - updateDateRange: UpdateDateRange; + title?: string | GetTitle; } export interface MatrixHistogramQueryProps { - activePage?: number; - dataKey: string; endDate: number; errorMessage: string; filterQuery?: ESQuery | string | undefined; - limit?: number; - query: DocumentNode; - sort?: NetworkDnsSortField; stackByField: string; - skip: boolean; startDate: number; - title: string | GetTitle; - isAlertsHistogram?: boolean; - isAnomaliesHistogram?: boolean; - isAuthenticationsHistogram?: boolean; - isDnsHistogram?: boolean; - isEventsHistogram?: boolean; isInspected: boolean; - isPtrIncluded?: boolean; - pagination?: PaginationInputPaginated; + histogramType: HistogramType; } export interface MatrixHistogramProps extends MatrixHistogramBasicProps { @@ -98,31 +94,38 @@ export interface HistogramAggregation { }; } -export interface SignalsResponse { - took: number; - timeout: boolean; -} - -export interface SignalSearchResponse - extends SignalsResponse { - _shards: { - total: number; - successful: number; - skipped: number; - failed: number; +export interface BarchartConfigs { + series: { + xScaleType: ScaleType; + yScaleType: ScaleType; + stackAccessors: string[]; }; - aggregations?: Aggregations; - hits: { - total: { - value: number; - relation: string; + axis: { + xTickFormatter: TickFormatter; + yTickFormatter: TickFormatter; + tickSize: number; + }; + settings: { + legendPosition: Position; + onBrushEnd: UpdateDateRange; + showLegend: boolean; + theme: { + scales: { + barsPadding: number; + }; + chartMargins: { + left: number; + right: number; + top: number; + bottom: number; + }; + chartPaddings: { + left: number; + right: number; + top: number; + bottom: number; + }; }; - hits: Hit[]; }; + customHeight: number; } - -export type Return = [ - boolean, - SignalSearchResponse | null, - React.Dispatch> -]; diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.test.ts b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.test.ts new file mode 100644 index 00000000000000..2c34a307bfdedd --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.test.ts @@ -0,0 +1,139 @@ +/* + * 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 { + getBarchartConfigs, + DEFAULT_CHART_HEIGHT, + DEFAULT_Y_TICK_FORMATTER, + formatToChartDataItem, + getCustomChartData, +} from './utils'; +import { UpdateDateRange } from '../charts/common'; +import { Position } from '@elastic/charts'; +import { MatrixOverTimeHistogramData } from '../../graphql/types'; +import { BarchartConfigs } from './types'; + +describe('utils', () => { + describe('getBarchartConfigs', () => { + describe('it should get correct default values', () => { + let configs: BarchartConfigs; + beforeAll(() => { + configs = getBarchartConfigs({ + from: 0, + to: 0, + onBrushEnd: jest.fn() as UpdateDateRange, + }); + }); + + test('it should set default chartHeight', () => { + expect(configs.customHeight).toEqual(DEFAULT_CHART_HEIGHT); + }); + + test('it should show legend by default', () => { + expect(configs.settings.showLegend).toEqual(true); + }); + + test('it should put legend on the right', () => { + expect(configs.settings.legendPosition).toEqual(Position.Right); + }); + + test('it should format Y tick to local string', () => { + expect(configs.axis.yTickFormatter).toEqual(DEFAULT_Y_TICK_FORMATTER); + }); + }); + + describe('it should set custom configs', () => { + let configs: BarchartConfigs; + const mockYTickFormatter = jest.fn(); + const mockChartHeight = 100; + + beforeAll(() => { + configs = getBarchartConfigs({ + chartHeight: mockChartHeight, + from: 0, + to: 0, + onBrushEnd: jest.fn() as UpdateDateRange, + yTickFormatter: mockYTickFormatter, + showLegend: false, + }); + }); + + test('it should set custom chart height', () => { + expect(configs.customHeight).toEqual(mockChartHeight); + }); + + test('it should hide legend', () => { + expect(configs.settings.showLegend).toEqual(false); + }); + + test('it should format y tick with custom formatter', () => { + expect(configs.axis.yTickFormatter).toEqual(mockYTickFormatter); + }); + }); + }); + + describe('formatToChartDataItem', () => { + test('it should format data correctly', () => { + const data: [string, MatrixOverTimeHistogramData[]] = [ + 'g1', + [ + { x: 1, y: 2, g: 'g1' }, + { x: 2, y: 4, g: 'g1' }, + { x: 3, y: 6, g: 'g1' }, + ], + ]; + const result = formatToChartDataItem(data); + expect(result).toEqual({ + key: 'g1', + value: [ + { x: 1, y: 2, g: 'g1' }, + { x: 2, y: 4, g: 'g1' }, + { x: 3, y: 6, g: 'g1' }, + ], + }); + }); + }); + + describe('getCustomChartData', () => { + test('should handle the case when no data provided', () => { + const data = null; + const result = getCustomChartData(data); + + expect(result).toEqual([]); + }); + + test('shoule format data correctly', () => { + const data = [ + { x: 1, y: 2, g: 'g1' }, + { x: 2, y: 4, g: 'g1' }, + { x: 3, y: 6, g: 'g1' }, + { x: 1, y: 1, g: 'g2' }, + { x: 2, y: 3, g: 'g2' }, + { x: 3, y: 5, g: 'g2' }, + ]; + const result = getCustomChartData(data); + + expect(result).toEqual([ + { + key: 'g1', + value: [ + { x: 1, y: 2, g: 'g1' }, + { x: 2, y: 4, g: 'g1' }, + { x: 3, y: 6, g: 'g1' }, + ], + }, + { + key: 'g2', + value: [ + { x: 1, y: 1, g: 'g2' }, + { x: 2, y: 3, g: 'g2' }, + { x: 3, y: 5, g: 'g2' }, + ], + }, + ]); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts index 95b1cd806cf6cb..ccd1b03eb54741 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts @@ -7,7 +7,8 @@ import { ScaleType, Position } from '@elastic/charts'; import { get, groupBy, map, toPairs } from 'lodash/fp'; import { UpdateDateRange, ChartSeriesData } from '../charts/common'; -import { MatrixHistogramDataTypes, MatrixHistogramMappingTypes } from './types'; +import { MatrixHistogramMappingTypes, BarchartConfigs } from './types'; +import { MatrixOverTimeHistogramData } from '../../graphql/types'; import { histogramDateTimeFormatter } from '../utils'; interface GetBarchartConfigsProps { @@ -15,40 +16,35 @@ interface GetBarchartConfigsProps { from: number; legendPosition?: Position; to: number; - scaleType: ScaleType; onBrushEnd: UpdateDateRange; yTickFormatter?: (value: number) => string; showLegend?: boolean; } export const DEFAULT_CHART_HEIGHT = 174; +export const DEFAULT_Y_TICK_FORMATTER = (value: string | number): string => value.toLocaleString(); export const getBarchartConfigs = ({ chartHeight, from, legendPosition, to, - scaleType, onBrushEnd, yTickFormatter, showLegend, -}: GetBarchartConfigsProps) => ({ +}: GetBarchartConfigsProps): BarchartConfigs => ({ series: { - xScaleType: scaleType || ScaleType.Time, + xScaleType: ScaleType.Time, yScaleType: ScaleType.Linear, stackAccessors: ['g'], }, axis: { - xTickFormatter: - scaleType === ScaleType.Time ? histogramDateTimeFormatter([from, to]) : undefined, - yTickFormatter: - yTickFormatter != null - ? yTickFormatter - : (value: string | number): string => value.toLocaleString(), + xTickFormatter: histogramDateTimeFormatter([from, to]), + yTickFormatter: yTickFormatter != null ? yTickFormatter : DEFAULT_Y_TICK_FORMATTER, tickSize: 8, }, settings: { - legendPosition: legendPosition ?? Position.Bottom, + legendPosition: legendPosition ?? Position.Right, onBrushEnd, showLegend: showLegend ?? true, theme: { @@ -74,14 +70,14 @@ export const getBarchartConfigs = ({ export const formatToChartDataItem = ([key, value]: [ string, - MatrixHistogramDataTypes[] + MatrixOverTimeHistogramData[] ]): ChartSeriesData => ({ key, value, }); export const getCustomChartData = ( - data: MatrixHistogramDataTypes[] | null, + data: MatrixOverTimeHistogramData[] | null, mapping?: MatrixHistogramMappingTypes ): ChartSeriesData[] => { if (!data) return []; @@ -92,7 +88,7 @@ export const getCustomChartData = ( if (mapping) return map((item: ChartSeriesData) => { const mapItem = get(item.key, mapping); - return { ...item, color: mapItem.color }; + return { ...item, color: mapItem?.color }; }, formattedChartData); else return formattedChartData; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/anomalies/anomalies_query_tab_body/histogram_configs.ts b/x-pack/legacy/plugins/siem/public/containers/anomalies/anomalies_query_tab_body/histogram_configs.ts new file mode 100644 index 00000000000000..f63349d3e573ad --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/anomalies/anomalies_query_tab_body/histogram_configs.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import * as i18n from './translations'; +import { + MatrixHistogramOption, + MatrixHisrogramConfigs, +} from '../../../components/matrix_histogram/types'; +import { HistogramType } from '../../../graphql/types'; + +export const anomaliesStackByOptions: MatrixHistogramOption[] = [ + { + text: i18n.ANOMALIES_STACK_BY_JOB_ID, + value: 'job_id', + }, +]; + +const DEFAULT_STACK_BY = i18n.ANOMALIES_STACK_BY_JOB_ID; + +export const histogramConfigs: MatrixHisrogramConfigs = { + defaultStackByOption: + anomaliesStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? anomaliesStackByOptions[0], + errorMessage: i18n.ERROR_FETCHING_ANOMALIES_DATA, + hideHistogramIfEmpty: true, + histogramType: HistogramType.anomalies, + stackByOptions: anomaliesStackByOptions, + subtitle: undefined, + title: i18n.ANOMALIES_TITLE, +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/anomalies/anomalies_query_tab_body/index.tsx b/x-pack/legacy/plugins/siem/public/containers/anomalies/anomalies_query_tab_body/index.tsx index e34832aa88c930..85e19248f2eb52 100644 --- a/x-pack/legacy/plugins/siem/public/containers/anomalies/anomalies_query_tab_body/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/anomalies/anomalies_query_tab_body/index.tsx @@ -5,23 +5,14 @@ */ import React, { useEffect } from 'react'; -import * as i18n from './translations'; import { AnomaliesQueryTabBodyProps } from './types'; import { getAnomaliesFilterQuery } from './utils'; import { useSiemJobs } from '../../../components/ml_popover/hooks/use_siem_jobs'; import { useUiSetting$ } from '../../../lib/kibana'; import { DEFAULT_ANOMALY_SCORE } from '../../../../common/constants'; -import { MatrixHistogramContainer } from '../../matrix_histogram'; -import { MatrixHistogramOption } from '../../../components/matrix_histogram/types'; -import { MatrixHistogramGqlQuery } from '../../matrix_histogram/index.gql_query'; - +import { MatrixHistogramContainer } from '../../../components/matrix_histogram'; +import { histogramConfigs } from './histogram_configs'; const ID = 'anomaliesOverTimeQuery'; -const anomaliesStackByOptions: MatrixHistogramOption[] = [ - { - text: i18n.ANOMALIES_STACK_BY_JOB_ID, - value: 'job_id', - }, -]; export const AnomaliesQueryTabBody = ({ deleteQuery, @@ -33,7 +24,6 @@ export const AnomaliesQueryTabBody = ({ narrowDateRange, filterQuery, anomaliesFilterQuery, - updateDateRange = () => {}, AnomaliesTableComponent, flowTarget, ip, @@ -61,23 +51,14 @@ export const AnomaliesQueryTabBody = ({ return ( <> ({ + useApolloClient: jest.fn(), +})); + +jest.mock('../../lib/kibana', () => { + return { + useUiSetting$: jest.fn().mockReturnValue(['mockDefaultIndex']), + }; +}); + +jest.mock('./index.gql_query', () => { + return { + MatrixHistogramGqlQuery: 'mockGqlQuery', + }; +}); + +jest.mock('../../components/ml/api/error_to_toaster'); + +describe('useQuery', () => { + let result: { + data: MatrixOverTimeHistogramData[] | null; + loading: boolean; + inspect: InspectQuery | null; + totalCount: number; + refetch: Refetch | undefined; + }; + describe('happy path', () => { + beforeAll(() => { + (useApolloClient as jest.Mock).mockReturnValue({ + query: mockQuery, + }); + const TestComponent = () => { + result = useQuery({ + endDate: 100, + errorMessage: 'fakeErrorMsg', + filterQuery: '', + histogramType: HistogramType.alerts, + isInspected: false, + stackByField: 'fakeField', + startDate: 0, + }); + + return
; + }; + + mount(); + }); + + test('should set variables', () => { + expect(mockQuery).toBeCalledWith({ + query: 'mockGqlQuery', + fetchPolicy: 'network-only', + variables: { + filterQuery: '', + sourceId: 'default', + timerange: { + interval: '12h', + from: 0, + to: 100, + }, + defaultIndex: 'mockDefaultIndex', + inspect: false, + stackByField: 'fakeField', + histogramType: 'alerts', + }, + context: { + fetchOptions: { + abortSignal: new AbortController().signal, + }, + }, + }); + }); + + test('should setData', () => { + expect(result.data).toEqual([{}]); + }); + + test('should set total count', () => { + expect(result.totalCount).toEqual(1); + }); + + test('should set inspect', () => { + expect(result.inspect).toEqual(false); + }); + }); + + describe('failure path', () => { + beforeAll(() => { + mockQuery.mockClear(); + (useApolloClient as jest.Mock).mockReset(); + (useApolloClient as jest.Mock).mockReturnValue({ + query: mockRejectQuery, + }); + const TestComponent = () => { + result = useQuery({ + endDate: 100, + errorMessage: 'fakeErrorMsg', + filterQuery: '', + histogramType: HistogramType.alerts, + isInspected: false, + stackByField: 'fakeField', + startDate: 0, + }); + + return
; + }; + + mount(); + }); + + test('should setData', () => { + expect(result.data).toEqual(null); + }); + + test('should set total count', () => { + expect(result.totalCount).toEqual(-1); + }); + + test('should set inspect', () => { + expect(result.inspect).toEqual(null); + }); + + test('should set error to toster', () => { + expect(errorToToaster).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/utils.ts b/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/index.ts similarity index 61% rename from x-pack/legacy/plugins/siem/public/containers/matrix_histogram/utils.ts rename to x-pack/legacy/plugins/siem/public/containers/matrix_histogram/index.ts index 1df1aec76627ce..683d5b68c305b4 100644 --- a/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/utils.ts +++ b/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/index.ts @@ -3,12 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { getOr } from 'lodash/fp'; -import { useEffect, useRef, useState } from 'react'; -import { - MatrixHistogramDataTypes, - MatrixHistogramQueryProps, -} from '../../components/matrix_histogram/types'; +import { useEffect, useState, useRef } from 'react'; +import { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { useStateToaster } from '../../components/toasters'; import { errorToToaster } from '../../components/ml/api/error_to_toaster'; @@ -16,20 +12,15 @@ import { useUiSetting$ } from '../../lib/kibana'; import { createFilter } from '../helpers'; import { useApolloClient } from '../../utils/apollo_context'; import { inputsModel } from '../../store'; -import { GetMatrixHistogramQuery } from '../../graphql/types'; +import { MatrixHistogramGqlQuery } from './index.gql_query'; +import { GetMatrixHistogramQuery, MatrixOverTimeHistogramData } from '../../graphql/types'; export const useQuery = ({ - dataKey, endDate, errorMessage, filterQuery, - isAlertsHistogram = false, - isAnomaliesHistogram = false, - isAuthenticationsHistogram = false, - isEventsHistogram = false, - isDnsHistogram = false, + histogramType, isInspected, - query, stackByField, startDate, }: MatrixHistogramQueryProps) => { @@ -37,30 +28,25 @@ export const useQuery = ({ const [, dispatchToaster] = useStateToaster(); const refetch = useRef(); const [loading, setLoading] = useState(false); - const [data, setData] = useState(null); + const [data, setData] = useState(null); const [inspect, setInspect] = useState(null); - const [totalCount, setTotalCount] = useState(-1); + const [totalCount, setTotalCount] = useState(-1); const apolloClient = useApolloClient(); - const matrixHistogramVariables: GetMatrixHistogramQuery.Variables = { - filterQuery: createFilter(filterQuery), - sourceId: 'default', - timerange: { - interval: '12h', - from: startDate!, - to: endDate!, - }, - defaultIndex, - inspect: isInspected, - stackByField, - isAlertsHistogram, - isAnomaliesHistogram, - isAuthenticationsHistogram, - isDnsHistogram, - isEventsHistogram, - }; - useEffect(() => { + const matrixHistogramVariables: GetMatrixHistogramQuery.Variables = { + filterQuery: createFilter(filterQuery), + sourceId: 'default', + timerange: { + interval: '12h', + from: startDate!, + to: endDate!, + }, + defaultIndex, + inspect: isInspected, + stackByField, + histogramType, + }; let isSubscribed = true; const abortCtrl = new AbortController(); const abortSignal = abortCtrl.signal; @@ -70,7 +56,7 @@ export const useQuery = ({ setLoading(true); return apolloClient .query({ - query, + query: MatrixHistogramGqlQuery, fetchPolicy: 'network-only', variables: matrixHistogramVariables, context: { @@ -82,13 +68,10 @@ export const useQuery = ({ .then( result => { if (isSubscribed) { - const isDataKeyAnArray = Array.isArray(dataKey); - const rootDataKey = isDataKeyAnArray ? dataKey[0] : `${dataKey}`; - const histogramDataKey = isDataKeyAnArray ? dataKey[1] : `matrixHistogramData`; - const source = getOr({}, `data.source.${rootDataKey}`, result); - setData(getOr([], histogramDataKey, source)); - setTotalCount(getOr(-1, 'totalCount', source)); - setInspect(getOr(null, 'inspect', source)); + const source = result?.data?.source?.MatrixHistogram ?? {}; + setData(source?.matrixHistogramData ?? []); + setTotalCount(source?.totalCount ?? -1); + setInspect(source?.inspect ?? null); setLoading(false); } }, @@ -97,8 +80,8 @@ export const useQuery = ({ setData(null); setTotalCount(-1); setInspect(null); - errorToToaster({ title: errorMessage, error, dispatchToaster }); setLoading(false); + errorToToaster({ title: errorMessage, error, dispatchToaster }); } } ); @@ -111,13 +94,14 @@ export const useQuery = ({ }; }, [ defaultIndex, - query, + errorMessage, filterQuery, + histogramType, isInspected, - isDnsHistogram, stackByField, startDate, endDate, + data, ]); return { data, loading, inspect, totalCount, refetch: refetch.current }; diff --git a/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/index.tsx deleted file mode 100644 index 9e0b1579a7b655..00000000000000 --- a/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/index.tsx +++ /dev/null @@ -1,66 +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 { Position } from '@elastic/charts'; -import React from 'react'; -import { compose } from 'redux'; - -import { connect } from 'react-redux'; -import { State, inputsSelectors, hostsModel, networkModel } from '../../store'; -import { QueryTemplateProps } from '../query_template'; - -import { Maybe } from '../../graphql/types'; -import { MatrixHistogram } from '../../components/matrix_histogram'; -import { - MatrixHistogramOption, - MatrixHistogramMappingTypes, - GetTitle, - GetSubTitle, -} from '../../components/matrix_histogram/types'; -import { UpdateDateRange } from '../../components/charts/common'; -import { SetQuery } from '../../pages/hosts/navigation/types'; - -export interface OwnProps extends QueryTemplateProps { - chartHeight?: number; - dataKey: string | string[]; - defaultStackByOption: MatrixHistogramOption; - errorMessage: string; - headerChildren?: React.ReactNode; - hideHistogramIfEmpty?: boolean; - isAlertsHistogram?: boolean; - isAnomaliesHistogram?: boolean; - isAuthenticationsHistogram?: boolean; - id: string; - isDnsHistogram?: boolean; - isEventsHistogram?: boolean; - legendPosition?: Position; - mapping?: MatrixHistogramMappingTypes; - panelHeight?: number; - query: Maybe; - setQuery: SetQuery; - showLegend?: boolean; - sourceId: string; - stackByOptions: MatrixHistogramOption[]; - subtitle?: string | GetSubTitle; - title: string | GetTitle; - type: hostsModel.HostsType | networkModel.NetworkType; - updateDateRange: UpdateDateRange; -} - -const makeMapStateToProps = () => { - const getQuery = inputsSelectors.globalQueryByIdSelector(); - const mapStateToProps = (state: State, { type, id }: OwnProps) => { - const { isInspected } = getQuery(state, id); - return { - isInspected, - }; - }; - return mapStateToProps; -}; - -export const MatrixHistogramContainer = compose>( - connect(makeMapStateToProps) -)(MatrixHistogram); diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json index b356b67b75c7bb..9802a5f5bd3bf7 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json @@ -666,112 +666,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "AlertsHistogram", - "description": "", - "args": [ - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "stackByField", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "AlertsOverTimeData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "AnomaliesHistogram", - "description": "", - "args": [ - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - }, - { - "name": "stackByField", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "AnomaliesOverTimeData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "Authentications", "description": "Gets Authentication success and failures based on a timerange", @@ -833,59 +727,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "AuthenticationsHistogram", - "description": "", - "args": [ - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - }, - { - "name": "stackByField", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "AuthenticationsOverTimeData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "Timeline", "description": "", @@ -1075,59 +916,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "EventsHistogram", - "description": "", - "args": [ - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - }, - { - "name": "stackByField", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "EventsOverTimeData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "Hosts", "description": "Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified", @@ -1610,6 +1398,73 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "MatrixHistogram", + "description": "", + "args": [ + { + "name": "filterQuery", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "defaultIndex", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, + "defaultValue": null + }, + { + "name": "timerange", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } + }, + "defaultValue": null + }, + { + "name": "stackByField", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null + }, + { + "name": "histogramType", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "HistogramType", "ofType": null } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "MatrixHistogramOverTimeData", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "NetworkTopCountries", "description": "", @@ -2607,211 +2462,17 @@ }, { "name": "description", - "description": "Description of the field", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "format", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "TimerangeInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "interval", - "description": "The interval string to use for last bucket. The format is '{value}{unit}'. For example '5m' would return the metrics for the last 5 minutes of the timespan.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "to", - "description": "The end of the timerange", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "from", - "description": "The beginning of the timerange", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AlertsOverTimeData", - "description": "", - "fields": [ - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "matrixHistogramData", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "totalCount", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Inspect", - "description": "", - "fields": [ - { - "name": "dsl", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "response", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", - "description": "", - "fields": [ - { - "name": "x", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "y", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, + "description": "Description of the field", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "g", + "name": "format", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -2822,57 +2483,43 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "AnomaliesOverTimeData", + "kind": "INPUT_OBJECT", + "name": "TimerangeInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "interval", + "description": "The interval string to use for last bucket. The format is '{value}{unit}'. For example '5m' would return the metrics for the last 5 minutes of the timespan.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null }, { - "name": "matrixHistogramData", - "description": "", - "args": [], + "name": "to", + "description": "The end of the timerange", "type": { "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", - "ofType": null - } - } - } + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "totalCount", - "description": "", - "args": [], + "name": "from", + "description": "The beginning of the timerange", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, @@ -3587,19 +3234,11 @@ }, { "kind": "OBJECT", - "name": "AuthenticationsOverTimeData", + "name": "Inspect", "description": "", "fields": [ { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "matrixHistogramData", + "name": "dsl", "description": "", "args": [], "type": { @@ -3611,11 +3250,7 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", - "ofType": null - } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } }, @@ -3623,13 +3258,21 @@ "deprecationReason": null }, { - "name": "totalCount", + "name": "response", "description": "", "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } }, "isDeprecated": false, "deprecationReason": null @@ -6639,61 +6282,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "EventsOverTimeData", - "description": "", - "fields": [ - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "matrixHistogramData", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "totalCount", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "INPUT_OBJECT", "name": "HostsSortField", @@ -7844,6 +7432,122 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "ENUM", + "name": "HistogramType", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "authentications", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "anomalies", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "events", "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "alerts", "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "dns", "description": "", "isDeprecated": false, "deprecationReason": null } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "MatrixHistogramOverTimeData", + "description": "", + "fields": [ + { + "name": "inspect", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "matrixHistogramData", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "MatrixOverTimeHistogramData", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "totalCount", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "MatrixOverTimeHistogramData", + "description": "", + "fields": [ + { + "name": "x", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "y", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "g", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "ENUM", "name": "FlowTargetSourceDest", diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts index 0103713a8c8a2d..3528ee6e13a389 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts @@ -301,6 +301,14 @@ export enum FlowTarget { source = 'source', } +export enum HistogramType { + authentications = 'authentications', + anomalies = 'anomalies', + events = 'events', + alerts = 'alerts', + dns = 'dns', +} + export enum FlowTargetSourceDest { destination = 'destination', source = 'source', @@ -460,22 +468,14 @@ export interface Source { configuration: SourceConfiguration; /** The status of the source */ status: SourceStatus; - - AlertsHistogram: AlertsOverTimeData; - - AnomaliesHistogram: AnomaliesOverTimeData; /** Gets Authentication success and failures based on a timerange */ Authentications: AuthenticationsData; - AuthenticationsHistogram: AuthenticationsOverTimeData; - Timeline: TimelineData; TimelineDetails: TimelineDetailsData; LastEventTime: LastEventTimeData; - - EventsHistogram: EventsOverTimeData; /** Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified */ Hosts: HostsData; @@ -493,6 +493,8 @@ export interface Source { KpiHostDetails: KpiHostDetailsData; + MatrixHistogram: MatrixHistogramOverTimeData; + NetworkTopCountries: NetworkTopCountriesData; NetworkTopNFlow: NetworkTopNFlowData; @@ -566,36 +568,6 @@ export interface IndexField { format?: Maybe; } -export interface AlertsOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; - - totalCount: number; -} - -export interface Inspect { - dsl: string[]; - - response: string[]; -} - -export interface MatrixOverTimeHistogramData { - x: number; - - y: number; - - g: string; -} - -export interface AnomaliesOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; - - totalCount: number; -} - export interface AuthenticationsData { edges: AuthenticationsEdges[]; @@ -730,12 +702,10 @@ export interface PageInfoPaginated { showMorePagesIndicator: boolean; } -export interface AuthenticationsOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; +export interface Inspect { + dsl: string[]; - totalCount: number; + response: string[]; } export interface TimelineData { @@ -1390,14 +1360,6 @@ export interface LastEventTimeData { inspect?: Maybe; } -export interface EventsOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; - - totalCount: number; -} - export interface HostsData { edges: HostsEdges[]; @@ -1598,6 +1560,22 @@ export interface KpiHostDetailsData { inspect?: Maybe; } +export interface MatrixHistogramOverTimeData { + inspect?: Maybe; + + matrixHistogramData: MatrixOverTimeHistogramData[]; + + totalCount: number; +} + +export interface MatrixOverTimeHistogramData { + x?: Maybe; + + y?: Maybe; + + g?: Maybe; +} + export interface NetworkTopCountriesData { edges: NetworkTopCountriesEdges[]; @@ -2241,24 +2219,6 @@ export interface GetAllTimelineQueryArgs { onlyUserFavorite?: Maybe; } -export interface AlertsHistogramSourceArgs { - filterQuery?: Maybe; - - defaultIndex: string[]; - - timerange: TimerangeInput; - - stackByField?: Maybe; -} -export interface AnomaliesHistogramSourceArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; -} export interface AuthenticationsSourceArgs { timerange: TimerangeInput; @@ -2268,15 +2228,6 @@ export interface AuthenticationsSourceArgs { defaultIndex: string[]; } -export interface AuthenticationsHistogramSourceArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; -} export interface TimelineSourceArgs { pagination: PaginationInput; @@ -2306,15 +2257,6 @@ export interface LastEventTimeSourceArgs { defaultIndex: string[]; } -export interface EventsHistogramSourceArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; -} export interface HostsSourceArgs { id?: Maybe; @@ -2397,6 +2339,17 @@ export interface KpiHostDetailsSourceArgs { defaultIndex: string[]; } +export interface MatrixHistogramSourceArgs { + filterQuery?: Maybe; + + defaultIndex: string[]; + + timerange: TimerangeInput; + + stackByField: string; + + histogramType: HistogramType; +} export interface NetworkTopCountriesSourceArgs { id?: Maybe; @@ -3330,16 +3283,12 @@ export namespace GetKpiNetworkQuery { export namespace GetMatrixHistogramQuery { export type Variables = { - isAlertsHistogram: boolean; - isAnomaliesHistogram: boolean; - isAuthenticationsHistogram: boolean; - isDnsHistogram: boolean; defaultIndex: string[]; - isEventsHistogram: boolean; filterQuery?: Maybe; + histogramType: HistogramType; inspect: boolean; sourceId: string; - stackByField?: Maybe; + stackByField: string; timerange: TimerangeInput; }; @@ -3354,19 +3303,11 @@ export namespace GetMatrixHistogramQuery { id: string; - AlertsHistogram: AlertsHistogram; - - AnomaliesHistogram: AnomaliesHistogram; - - AuthenticationsHistogram: AuthenticationsHistogram; - - EventsHistogram: EventsHistogram; - - NetworkDnsHistogram: NetworkDnsHistogram; + MatrixHistogram: MatrixHistogram; }; - export type AlertsHistogram = { - __typename?: 'AlertsOverTimeData'; + export type MatrixHistogram = { + __typename?: 'MatrixHistogramOverTimeData'; matrixHistogramData: MatrixHistogramData[]; @@ -3378,11 +3319,11 @@ export namespace GetMatrixHistogramQuery { export type MatrixHistogramData = { __typename?: 'MatrixOverTimeHistogramData'; - x: number; + x: Maybe; - y: number; + y: Maybe; - g: string; + g: Maybe; }; export type Inspect = { @@ -3392,118 +3333,6 @@ export namespace GetMatrixHistogramQuery { response: string[]; }; - - export type AnomaliesHistogram = { - __typename?: 'AnomaliesOverTimeData'; - - matrixHistogramData: _MatrixHistogramData[]; - - totalCount: number; - - inspect: Maybe<_Inspect>; - }; - - export type _MatrixHistogramData = { - __typename?: 'MatrixOverTimeHistogramData'; - - x: number; - - y: number; - - g: string; - }; - - export type _Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; - - export type AuthenticationsHistogram = { - __typename?: 'AuthenticationsOverTimeData'; - - matrixHistogramData: __MatrixHistogramData[]; - - totalCount: number; - - inspect: Maybe<__Inspect>; - }; - - export type __MatrixHistogramData = { - __typename?: 'MatrixOverTimeHistogramData'; - - x: number; - - y: number; - - g: string; - }; - - export type __Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; - - export type EventsHistogram = { - __typename?: 'EventsOverTimeData'; - - matrixHistogramData: ___MatrixHistogramData[]; - - totalCount: number; - - inspect: Maybe<___Inspect>; - }; - - export type ___MatrixHistogramData = { - __typename?: 'MatrixOverTimeHistogramData'; - - x: number; - - y: number; - - g: string; - }; - - export type ___Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; - - export type NetworkDnsHistogram = { - __typename?: 'NetworkDsOverTimeData'; - - matrixHistogramData: ____MatrixHistogramData[]; - - totalCount: number; - - inspect: Maybe<____Inspect>; - }; - - export type ____MatrixHistogramData = { - __typename?: 'MatrixOverTimeHistogramData'; - - x: number; - - y: number; - - g: string; - }; - - export type ____Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; } export namespace GetNetworkDnsQuery { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx index f5a63456ad5d9b..c3fb907ae83e1e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx @@ -173,7 +173,6 @@ const DetectionEnginePageComponent: React.FC = ({ hideHeaderChildren={true} indexPattern={indexPattern} query={query} - setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker!} setQuery={setQuery} to={to} /> diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index 0d86bf9f964d4d..a7aa9920b7d083 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -27,6 +27,7 @@ import { useKibana } from '../../lib/kibana'; import { convertToBuildEsQuery } from '../../lib/keury'; import { inputsSelectors, State, hostsModel } from '../../store'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; + import { SpyRoute } from '../../utils/route/spy_routes'; import { esQuery } from '../../../../../../../src/plugins/data/public'; import { HostsEmptyPage } from './hosts_empty_page'; @@ -129,11 +130,11 @@ export const HostsComponent = React.memo( to={to} filterQuery={tabsFilterQuery} isInitializing={isInitializing} + setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} setQuery={setQuery} from={from} type={hostsModel.HostsType.page} indexPattern={indexPattern} - setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} hostsPagePath={hostsPagePath} /> diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/authentications_query_tab_body.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/authentications_query_tab_body.tsx index a6a0344599842a..fb083b7a7da2f3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/authentications_query_tab_body.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/authentications_query_tab_body.tsx @@ -14,11 +14,12 @@ import { hostsModel } from '../../../store/hosts'; import { MatrixHistogramOption, MatrixHistogramMappingTypes, + MatrixHisrogramConfigs, } from '../../../components/matrix_histogram/types'; -import { MatrixHistogramContainer } from '../../../containers/matrix_histogram'; +import { MatrixHistogramContainer } from '../../../components/matrix_histogram'; import { KpiHostsChartColors } from '../../../components/page/hosts/kpi_hosts/types'; -import { MatrixHistogramGqlQuery } from '../../../containers/matrix_histogram/index.gql_query'; import * as i18n from '../translations'; +import { HistogramType } from '../../../graphql/types'; const AuthenticationTableManage = manageQuery(AuthenticationTable); const ID = 'authenticationsOverTimeQuery'; @@ -28,6 +29,7 @@ const authStackByOptions: MatrixHistogramOption[] = [ value: 'event.type', }, ]; +const DEFAULT_STACK_BY = 'event.type'; enum AuthMatrixDataGroup { authSuccess = 'authentication_success', @@ -47,6 +49,16 @@ export const authMatrixDataMappingFields: MatrixHistogramMappingTypes = { }, }; +const histogramConfigs: MatrixHisrogramConfigs = { + defaultStackByOption: + authStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? authStackByOptions[0], + errorMessage: i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA, + histogramType: HistogramType.authentications, + mapping: authMatrixDataMappingFields, + stackByOptions: authStackByOptions, + title: i18n.NAVIGATION_AUTHENTICATIONS_TITLE, +}; + export const AuthenticationsQueryTabBody = ({ deleteQuery, endDate, @@ -55,7 +67,6 @@ export const AuthenticationsQueryTabBody = ({ setQuery, startDate, type, - updateDateRange = () => {}, }: HostsComponentsQueryProps) => { useEffect(() => { return () => { @@ -64,26 +75,18 @@ export const AuthenticationsQueryTabBody = ({ } }; }, [deleteQuery]); + return ( <> o.text === DEFAULT_STACK_BY) ?? eventsStackByOptions[0], + errorMessage: i18n.ERROR_FETCHING_EVENTS_DATA, + histogramType: HistogramType.events, + stackByOptions: eventsStackByOptions, + subtitle: undefined, + title: i18n.NAVIGATION_EVENTS_TITLE, +}; + export const EventsQueryTabBody = ({ deleteQuery, endDate, filterQuery, pageFilters, setQuery, - skip, startDate, - updateDateRange = () => {}, }: HostsComponentsQueryProps) => { useEffect(() => { return () => { @@ -49,25 +62,18 @@ export const EventsQueryTabBody = ({ } }; }, [deleteQuery]); + return ( <> = { + defaultStackByOption: + dnsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? dnsStackByOptions[0], + errorMessage: i18n.ERROR_FETCHING_DNS_DATA, + histogramType: HistogramType.dns, + stackByOptions: dnsStackByOptions, + subtitle: undefined, +}; + export const DnsQueryTabBody = ({ deleteQuery, endDate, @@ -36,7 +50,6 @@ export const DnsQueryTabBody = ({ startDate, setQuery, type, - updateDateRange = () => {}, }: NetworkComponentQueryProps) => { useEffect(() => { return () => { @@ -51,24 +64,26 @@ export const DnsQueryTabBody = ({ [] ); + const dnsHistogramConfigs: MatrixHisrogramConfigs = useMemo( + () => ({ + ...histogramConfigs, + title: getTitle, + }), + [getTitle] + ); + return ( <> { @@ -22,7 +21,6 @@ interface QueryTabBodyProps extends Pick = ({ hideHeaderChildren = false, indexPattern, query = DEFAULT_QUERY, - setAbsoluteRangeDatePicker, setQuery, to, }) => { @@ -77,32 +74,26 @@ const AlertsByCategoryComponent: React.FC = ({ const kibana = useKibana(); const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const updateDateRangeCallback = useCallback( - (min: number, max: number) => { - setAbsoluteRangeDatePicker!({ id: 'global', from: min, to: max }); - }, - [setAbsoluteRangeDatePicker] - ); const alertsCountViewAlertsButton = useMemo( () => {i18n.VIEW_ALERTS}, [] ); - const getSubtitle = useCallback( - (totalCount: number) => - `${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`, + const alertsByCategoryHistogramConfigs: MatrixHisrogramConfigs = useMemo( + () => ({ + ...histogramConfigs, + defaultStackByOption: + alertsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0], + getSubtitle: (totalCount: number) => + `${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`, + legendPosition: Position.Right, + }), [] ); - const defaultStackByOption = - alertsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0]; - return ( = ({ })} headerChildren={hideHeaderChildren ? null : alertsCountViewAlertsButton} id={ID} - isAlertsHistogram={true} - legendPosition={'right'} - query={MatrixHistogramGqlQuery} setQuery={setQuery} sourceId="default" - stackByOptions={alertsStackByOptions} startDate={from} - title={i18n.ALERTS_GRAPH_TITLE} - subtitle={getSubtitle} type={HostsType.page} - updateDateRange={updateDateRangeCallback} + {...alertsByCategoryHistogramConfigs} /> ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx index 5b6ad69bcb15d0..315aac5fcae9ef 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx @@ -6,18 +6,14 @@ import { EuiButton } from '@elastic/eui'; import numeral from '@elastic/numeral'; -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; -import { - ERROR_FETCHING_EVENTS_DATA, - SHOWING, - UNIT, -} from '../../../components/events_viewer/translations'; +import { Position } from '@elastic/charts'; +import { SHOWING, UNIT } from '../../../components/events_viewer/translations'; import { convertToBuildEsQuery } from '../../../lib/keury'; -import { SetAbsoluteRangeDatePicker } from '../../network/types'; import { getTabsOnHostsUrl } from '../../../components/link_to/redirect_to_hosts'; -import { MatrixHistogramContainer } from '../../../containers/matrix_histogram'; -import { MatrixHistogramGqlQuery } from '../../../containers/matrix_histogram/index.gql_query'; +import { histogramConfigs } from '../../../pages/hosts/navigation/events_query_tab_body'; +import { MatrixHistogramContainer } from '../../../components/matrix_histogram'; import { eventsStackByOptions } from '../../hosts/navigation'; import { useKibana, useUiSetting$ } from '../../../lib/kibana'; import { @@ -31,6 +27,7 @@ import { HostsTableType, HostsType } from '../../../store/hosts/model'; import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; import * as i18n from '../translations'; +import { MatrixHisrogramConfigs } from '../../../components/matrix_histogram/types'; const NO_FILTERS: Filter[] = []; const DEFAULT_QUERY: Query = { query: '', language: 'kuery' }; @@ -44,7 +41,6 @@ interface Props { from: number; indexPattern: IIndexPattern; query?: Query; - setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker; setQuery: (params: { id: string; inspect: inputsModel.InspectQuery | null; @@ -60,7 +56,6 @@ const EventsByDatasetComponent: React.FC = ({ from, indexPattern, query = DEFAULT_QUERY, - setAbsoluteRangeDatePicker, setQuery, to, }) => { @@ -70,31 +65,16 @@ const EventsByDatasetComponent: React.FC = ({ deleteQuery({ id: ID }); } }; - }, []); + }, [deleteQuery]); const kibana = useKibana(); const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const updateDateRangeCallback = useCallback( - (min: number, max: number) => { - setAbsoluteRangeDatePicker!({ id: 'global', from: min, to: max }); - }, - [setAbsoluteRangeDatePicker] - ); const eventsCountViewEventsButton = useMemo( () => {i18n.VIEW_EVENTS}, [] ); - const getSubtitle = useCallback( - (totalCount: number) => - `${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`, - [] - ); - - const defaultStackByOption = - eventsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? eventsStackByOptions[0]; - const filterQuery = useMemo( () => convertToBuildEsQuery({ @@ -106,26 +86,29 @@ const EventsByDatasetComponent: React.FC = ({ [kibana, indexPattern, query, filters] ); + const eventsByDatasetHistogramConfigs: MatrixHisrogramConfigs = useMemo( + () => ({ + ...histogramConfigs, + defaultStackByOption: + eventsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? eventsStackByOptions[0], + legendPosition: Position.Right, + subtitle: (totalCount: number) => + `${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`, + }), + [] + ); + return ( ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx index 9ddfeff4b403bc..2db49e60193fcc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx @@ -77,7 +77,6 @@ const OverviewComponent: React.FC = ({ from={from} indexPattern={indexPattern} query={query} - setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker!} setQuery={setQuery} to={to} /> @@ -90,7 +89,6 @@ const OverviewComponent: React.FC = ({ from={from} indexPattern={indexPattern} query={query} - setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker!} setQuery={setQuery} to={to} /> diff --git a/x-pack/legacy/plugins/siem/server/graphql/anomalies/resolvers.ts b/x-pack/legacy/plugins/siem/server/graphql/anomalies/resolvers.ts deleted file mode 100644 index e7b7a640c58d23..00000000000000 --- a/x-pack/legacy/plugins/siem/server/graphql/anomalies/resolvers.ts +++ /dev/null @@ -1,39 +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 { Anomalies } from '../../lib/anomalies'; -import { AppResolverOf, ChildResolverOf } from '../../lib/framework'; -import { createOptions } from '../../utils/build_query/create_options'; -import { QuerySourceResolver } from '../sources/resolvers'; -import { SourceResolvers } from '../types'; - -export interface AnomaliesResolversDeps { - anomalies: Anomalies; -} - -type QueryAnomaliesOverTimeResolver = ChildResolverOf< - AppResolverOf, - QuerySourceResolver ->; - -export const createAnomaliesResolvers = ( - libs: AnomaliesResolversDeps -): { - Source: { - AnomaliesHistogram: QueryAnomaliesOverTimeResolver; - }; -} => ({ - Source: { - async AnomaliesHistogram(source, args, { req }, info) { - const options = { - ...createOptions(source, args, info), - defaultIndex: args.defaultIndex, - stackByField: args.stackByField, - }; - return libs.anomalies.getAnomaliesOverTime(req, options); - }, - }, -}); diff --git a/x-pack/legacy/plugins/siem/server/graphql/anomalies/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/anomalies/schema.gql.ts deleted file mode 100644 index a0b834f705696c..00000000000000 --- a/x-pack/legacy/plugins/siem/server/graphql/anomalies/schema.gql.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const anomaliesSchema = gql` - type AnomaliesOverTimeData { - inspect: Inspect - matrixHistogramData: [MatrixOverTimeHistogramData!]! - totalCount: Float! - } - - extend type Source { - AnomaliesHistogram( - timerange: TimerangeInput! - filterQuery: String - defaultIndex: [String!]! - stackByField: String - ): AnomaliesOverTimeData! - } -`; diff --git a/x-pack/legacy/plugins/siem/server/graphql/authentications/resolvers.ts b/x-pack/legacy/plugins/siem/server/graphql/authentications/resolvers.ts index ce1c86ac8926c4..b66ccd9a111b70 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/authentications/resolvers.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/authentications/resolvers.ts @@ -7,7 +7,7 @@ import { SourceResolvers } from '../../graphql/types'; import { Authentications } from '../../lib/authentications'; import { AppResolverOf, ChildResolverOf } from '../../lib/framework'; -import { createOptionsPaginated, createOptions } from '../../utils/build_query/create_options'; +import { createOptionsPaginated } from '../../utils/build_query/create_options'; import { QuerySourceResolver } from '../sources/resolvers'; type QueryAuthenticationsResolver = ChildResolverOf< @@ -15,11 +15,6 @@ type QueryAuthenticationsResolver = ChildResolverOf< QuerySourceResolver >; -type QueryAuthenticationsOverTimeResolver = ChildResolverOf< - AppResolverOf, - QuerySourceResolver ->; - export interface AuthenticationsResolversDeps { authentications: Authentications; } @@ -29,7 +24,6 @@ export const createAuthenticationsResolvers = ( ): { Source: { Authentications: QueryAuthenticationsResolver; - AuthenticationsHistogram: QueryAuthenticationsOverTimeResolver; }; } => ({ Source: { @@ -37,13 +31,5 @@ export const createAuthenticationsResolvers = ( const options = createOptionsPaginated(source, args, info); return libs.authentications.getAuthentications(req, options); }, - async AuthenticationsHistogram(source, args, { req }, info) { - const options = { - ...createOptions(source, args, info), - defaultIndex: args.defaultIndex, - stackByField: args.stackByField, - }; - return libs.authentications.getAuthenticationsOverTime(req, options); - }, }, }); diff --git a/x-pack/legacy/plugins/siem/server/graphql/authentications/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/authentications/schema.gql.ts index 4acc72a5b0b6f1..20935ce9ed03fc 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/authentications/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/authentications/schema.gql.ts @@ -34,12 +34,6 @@ export const authenticationsSchema = gql` inspect: Inspect } - type AuthenticationsOverTimeData { - inspect: Inspect - matrixHistogramData: [MatrixOverTimeHistogramData!]! - totalCount: Float! - } - extend type Source { "Gets Authentication success and failures based on a timerange" Authentications( @@ -48,11 +42,5 @@ export const authenticationsSchema = gql` filterQuery: String defaultIndex: [String!]! ): AuthenticationsData! - AuthenticationsHistogram( - timerange: TimerangeInput! - filterQuery: String - defaultIndex: [String!]! - stackByField: String - ): AuthenticationsOverTimeData! } `; diff --git a/x-pack/legacy/plugins/siem/server/graphql/events/resolvers.ts b/x-pack/legacy/plugins/siem/server/graphql/events/resolvers.ts index 335f4c3bf4da37..a9ef6bc682c845 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/events/resolvers.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/events/resolvers.ts @@ -31,12 +31,6 @@ type QueryLastEventTimeResolver = ChildResolverOf< export interface EventsResolversDeps { events: Events; } - -type QueryEventsOverTimeResolver = ChildResolverOf< - AppResolverOf, - QuerySourceResolver ->; - export const createEventsResolvers = ( libs: EventsResolversDeps ): { @@ -44,7 +38,6 @@ export const createEventsResolvers = ( Timeline: QueryTimelineResolver; TimelineDetails: QueryTimelineDetailsResolver; LastEventTime: QueryLastEventTimeResolver; - EventsHistogram: QueryEventsOverTimeResolver; }; } => ({ Source: { @@ -71,14 +64,6 @@ export const createEventsResolvers = ( }; return libs.events.getLastEventTimeData(req, options); }, - async EventsHistogram(source, args, { req }, info) { - const options = { - ...createOptions(source, args, info), - defaultIndex: args.defaultIndex, - stackByField: args.stackByField, - }; - return libs.events.getEventsOverTime(req, options); - }, }, }); diff --git a/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts index 9b321d10614fc2..3b71977bc0d478 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts @@ -68,18 +68,6 @@ export const eventsSchema = gql` network } - type MatrixOverTimeHistogramData { - x: Float! - y: Float! - g: String! - } - - type EventsOverTimeData { - inspect: Inspect - matrixHistogramData: [MatrixOverTimeHistogramData!]! - totalCount: Float! - } - extend type Source { Timeline( pagination: PaginationInput! @@ -100,11 +88,5 @@ export const eventsSchema = gql` details: LastTimeDetails! defaultIndex: [String!]! ): LastEventTimeData! - EventsHistogram( - timerange: TimerangeInput! - filterQuery: String - defaultIndex: [String!]! - stackByField: String - ): EventsOverTimeData! } `; diff --git a/x-pack/legacy/plugins/siem/server/graphql/index.ts b/x-pack/legacy/plugins/siem/server/graphql/index.ts index 60853e2ce7bed4..7e257357078939 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/index.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/index.ts @@ -7,7 +7,6 @@ import { rootSchema } from '../../common/graphql/root'; import { sharedSchema } from '../../common/graphql/shared'; -import { anomaliesSchema } from './anomalies'; import { authenticationsSchema } from './authentications'; import { ecsSchema } from './ecs'; import { eventsSchema } from './events'; @@ -30,10 +29,8 @@ import { timelineSchema } from './timeline'; import { tlsSchema } from './tls'; import { uncommonProcessesSchema } from './uncommon_processes'; import { whoAmISchema } from './who_am_i'; -import { alertsSchema } from './alerts'; +import { matrixHistogramSchema } from './matrix_histogram'; export const schemas = [ - alertsSchema, - anomaliesSchema, authenticationsSchema, ecsSchema, eventsSchema, @@ -46,6 +43,7 @@ export const schemas = [ ...ipDetailsSchemas, kpiNetworkSchema, kpiHostsSchema, + matrixHistogramSchema, networkSchema, noteSchema, overviewSchema, diff --git a/x-pack/legacy/plugins/index_management/public/shared_imports.ts b/x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/index.ts similarity index 56% rename from x-pack/legacy/plugins/index_management/public/shared_imports.ts rename to x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/index.ts index cbc4dde7448ff0..1460b6022bb133 100644 --- a/x-pack/legacy/plugins/index_management/public/shared_imports.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/index.ts @@ -4,10 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - SendRequestConfig, - SendRequestResponse, - UseRequestConfig, - sendRequest, - useRequest, -} from '../../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; +export { createMatrixHistogramResolvers } from './resolvers'; +export { matrixHistogramSchema } from './schema.gql'; diff --git a/x-pack/legacy/plugins/siem/server/graphql/alerts/resolvers.ts b/x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/resolvers.ts similarity index 55% rename from x-pack/legacy/plugins/siem/server/graphql/alerts/resolvers.ts rename to x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/resolvers.ts index 5a3a50d5c6ec68..35cebe4777dcf3 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/alerts/resolvers.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/resolvers.ts @@ -4,36 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alerts } from '../../lib/alerts'; +import { MatrixHistogram } from '../../lib/matrix_histogram'; import { AppResolverOf, ChildResolverOf } from '../../lib/framework'; import { createOptions } from '../../utils/build_query/create_options'; import { QuerySourceResolver } from '../sources/resolvers'; import { SourceResolvers } from '../types'; -export interface AlertsResolversDeps { - alerts: Alerts; +export interface MatrixHistogramResolversDeps { + matrixHistogram: MatrixHistogram; } -type QueryAlertsHistogramResolver = ChildResolverOf< - AppResolverOf, +type QueryMatrixHistogramResolver = ChildResolverOf< + AppResolverOf, QuerySourceResolver >; -export const createAlertsResolvers = ( - libs: AlertsResolversDeps +export const createMatrixHistogramResolvers = ( + libs: MatrixHistogramResolversDeps ): { Source: { - AlertsHistogram: QueryAlertsHistogramResolver; + MatrixHistogram: QueryMatrixHistogramResolver; }; } => ({ Source: { - async AlertsHistogram(source, args, { req }, info) { + async MatrixHistogram(source, args, { req }, info) { const options = { ...createOptions(source, args, info), - defaultIndex: args.defaultIndex, stackByField: args.stackByField, + histogramType: args.histogramType, }; - return libs.alerts.getAlertsHistogramData(req, options); + return libs.matrixHistogram.getMatrixHistogramData(req, options); }, }, }); diff --git a/x-pack/legacy/plugins/siem/server/graphql/alerts/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/schema.gql.ts similarity index 57% rename from x-pack/legacy/plugins/siem/server/graphql/alerts/schema.gql.ts rename to x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/schema.gql.ts index ca91468b1e0f22..deda6dc6e5c1a7 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/alerts/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/matrix_histogram/schema.gql.ts @@ -6,19 +6,34 @@ import gql from 'graphql-tag'; -export const alertsSchema = gql` - type AlertsOverTimeData { +export const matrixHistogramSchema = gql` + type MatrixOverTimeHistogramData { + x: Float + y: Float + g: String + } + + type MatrixHistogramOverTimeData { inspect: Inspect matrixHistogramData: [MatrixOverTimeHistogramData!]! totalCount: Float! } + enum HistogramType { + authentications + anomalies + events + alerts + dns + } + extend type Source { - AlertsHistogram( + MatrixHistogram( filterQuery: String defaultIndex: [String!]! timerange: TimerangeInput! - stackByField: String - ): AlertsOverTimeData! + stackByField: String! + histogramType: HistogramType! + ): MatrixHistogramOverTimeData! } `; diff --git a/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts b/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts index 06d6b8c516d8bd..db15babc42a722 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts @@ -7,7 +7,7 @@ import { SourceResolvers } from '../../graphql/types'; import { AppResolverOf, ChildResolverOf } from '../../lib/framework'; import { Network } from '../../lib/network'; -import { createOptionsPaginated, createOptions } from '../../utils/build_query/create_options'; +import { createOptionsPaginated } from '../../utils/build_query/create_options'; import { QuerySourceResolver } from '../sources/resolvers'; type QueryNetworkTopCountriesResolver = ChildResolverOf< @@ -30,10 +30,6 @@ type QueryDnsResolver = ChildResolverOf< QuerySourceResolver >; -type QueryDnsHistogramResolver = ChildResolverOf< - AppResolverOf, - QuerySourceResolver ->; export interface NetworkResolversDeps { network: Network; } @@ -46,7 +42,6 @@ export const createNetworkResolvers = ( NetworkTopCountries: QueryNetworkTopCountriesResolver; NetworkTopNFlow: QueryNetworkTopNFlowResolver; NetworkDns: QueryDnsResolver; - NetworkDnsHistogram: QueryDnsHistogramResolver; }; } => ({ Source: { @@ -84,12 +79,5 @@ export const createNetworkResolvers = ( }; return libs.network.getNetworkDns(req, options); }, - async NetworkDnsHistogram(source, args, { req }, info) { - const options = { - ...createOptions(source, args, info), - stackByField: args.stackByField, - }; - return libs.network.getNetworkDnsHistogramData(req, options); - }, }, }); diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts index c3fd6e9dde2865..f42da48f2c1dac 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts @@ -303,6 +303,14 @@ export enum FlowTarget { source = 'source', } +export enum HistogramType { + authentications = 'authentications', + anomalies = 'anomalies', + events = 'events', + alerts = 'alerts', + dns = 'dns', +} + export enum FlowTargetSourceDest { destination = 'destination', source = 'source', @@ -462,22 +470,14 @@ export interface Source { configuration: SourceConfiguration; /** The status of the source */ status: SourceStatus; - - AlertsHistogram: AlertsOverTimeData; - - AnomaliesHistogram: AnomaliesOverTimeData; /** Gets Authentication success and failures based on a timerange */ Authentications: AuthenticationsData; - AuthenticationsHistogram: AuthenticationsOverTimeData; - Timeline: TimelineData; TimelineDetails: TimelineDetailsData; LastEventTime: LastEventTimeData; - - EventsHistogram: EventsOverTimeData; /** Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified */ Hosts: HostsData; @@ -495,6 +495,8 @@ export interface Source { KpiHostDetails: KpiHostDetailsData; + MatrixHistogram: MatrixHistogramOverTimeData; + NetworkTopCountries: NetworkTopCountriesData; NetworkTopNFlow: NetworkTopNFlowData; @@ -568,36 +570,6 @@ export interface IndexField { format?: Maybe; } -export interface AlertsOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; - - totalCount: number; -} - -export interface Inspect { - dsl: string[]; - - response: string[]; -} - -export interface MatrixOverTimeHistogramData { - x: number; - - y: number; - - g: string; -} - -export interface AnomaliesOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; - - totalCount: number; -} - export interface AuthenticationsData { edges: AuthenticationsEdges[]; @@ -732,12 +704,10 @@ export interface PageInfoPaginated { showMorePagesIndicator: boolean; } -export interface AuthenticationsOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; +export interface Inspect { + dsl: string[]; - totalCount: number; + response: string[]; } export interface TimelineData { @@ -1392,14 +1362,6 @@ export interface LastEventTimeData { inspect?: Maybe; } -export interface EventsOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; - - totalCount: number; -} - export interface HostsData { edges: HostsEdges[]; @@ -1600,6 +1562,22 @@ export interface KpiHostDetailsData { inspect?: Maybe; } +export interface MatrixHistogramOverTimeData { + inspect?: Maybe; + + matrixHistogramData: MatrixOverTimeHistogramData[]; + + totalCount: number; +} + +export interface MatrixOverTimeHistogramData { + x?: Maybe; + + y?: Maybe; + + g?: Maybe; +} + export interface NetworkTopCountriesData { edges: NetworkTopCountriesEdges[]; @@ -2243,24 +2221,6 @@ export interface GetAllTimelineQueryArgs { onlyUserFavorite?: Maybe; } -export interface AlertsHistogramSourceArgs { - filterQuery?: Maybe; - - defaultIndex: string[]; - - timerange: TimerangeInput; - - stackByField?: Maybe; -} -export interface AnomaliesHistogramSourceArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; -} export interface AuthenticationsSourceArgs { timerange: TimerangeInput; @@ -2270,15 +2230,6 @@ export interface AuthenticationsSourceArgs { defaultIndex: string[]; } -export interface AuthenticationsHistogramSourceArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; -} export interface TimelineSourceArgs { pagination: PaginationInput; @@ -2308,15 +2259,6 @@ export interface LastEventTimeSourceArgs { defaultIndex: string[]; } -export interface EventsHistogramSourceArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; -} export interface HostsSourceArgs { id?: Maybe; @@ -2399,6 +2341,17 @@ export interface KpiHostDetailsSourceArgs { defaultIndex: string[]; } +export interface MatrixHistogramSourceArgs { + filterQuery?: Maybe; + + defaultIndex: string[]; + + timerange: TimerangeInput; + + stackByField: string; + + histogramType: HistogramType; +} export interface NetworkTopCountriesSourceArgs { id?: Maybe; @@ -2910,26 +2863,14 @@ export namespace SourceResolvers { configuration?: ConfigurationResolver; /** The status of the source */ status?: StatusResolver; - - AlertsHistogram?: AlertsHistogramResolver; - - AnomaliesHistogram?: AnomaliesHistogramResolver; /** Gets Authentication success and failures based on a timerange */ Authentications?: AuthenticationsResolver; - AuthenticationsHistogram?: AuthenticationsHistogramResolver< - AuthenticationsOverTimeData, - TypeParent, - TContext - >; - Timeline?: TimelineResolver; TimelineDetails?: TimelineDetailsResolver; LastEventTime?: LastEventTimeResolver; - - EventsHistogram?: EventsHistogramResolver; /** Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified */ Hosts?: HostsResolver; @@ -2947,6 +2888,8 @@ export namespace SourceResolvers { KpiHostDetails?: KpiHostDetailsResolver; + MatrixHistogram?: MatrixHistogramResolver; + NetworkTopCountries?: NetworkTopCountriesResolver< NetworkTopCountriesData, TypeParent, @@ -2987,36 +2930,6 @@ export namespace SourceResolvers { Parent, TContext >; - export type AlertsHistogramResolver< - R = AlertsOverTimeData, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface AlertsHistogramArgs { - filterQuery?: Maybe; - - defaultIndex: string[]; - - timerange: TimerangeInput; - - stackByField?: Maybe; - } - - export type AnomaliesHistogramResolver< - R = AnomaliesOverTimeData, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface AnomaliesHistogramArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; - } - export type AuthenticationsResolver< R = AuthenticationsData, Parent = Source, @@ -3032,21 +2945,6 @@ export namespace SourceResolvers { defaultIndex: string[]; } - export type AuthenticationsHistogramResolver< - R = AuthenticationsOverTimeData, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface AuthenticationsHistogramArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; - } - export type TimelineResolver< R = TimelineData, Parent = Source, @@ -3094,21 +2992,6 @@ export namespace SourceResolvers { defaultIndex: string[]; } - export type EventsHistogramResolver< - R = EventsOverTimeData, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface EventsHistogramArgs { - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - stackByField?: Maybe; - } - export type HostsResolver = Resolver< R, Parent, @@ -3241,6 +3124,23 @@ export namespace SourceResolvers { defaultIndex: string[]; } + export type MatrixHistogramResolver< + R = MatrixHistogramOverTimeData, + Parent = Source, + TContext = SiemContext + > = Resolver; + export interface MatrixHistogramArgs { + filterQuery?: Maybe; + + defaultIndex: string[]; + + timerange: TimerangeInput; + + stackByField: string; + + histogramType: HistogramType; + } + export type NetworkTopCountriesResolver< R = NetworkTopCountriesData, Parent = Source, @@ -3579,111 +3479,6 @@ export namespace IndexFieldResolvers { > = Resolver; } -export namespace AlertsOverTimeDataResolvers { - export interface Resolvers { - inspect?: InspectResolver, TypeParent, TContext>; - - matrixHistogramData?: MatrixHistogramDataResolver< - MatrixOverTimeHistogramData[], - TypeParent, - TContext - >; - - totalCount?: TotalCountResolver; - } - - export type InspectResolver< - R = Maybe, - Parent = AlertsOverTimeData, - TContext = SiemContext - > = Resolver; - export type MatrixHistogramDataResolver< - R = MatrixOverTimeHistogramData[], - Parent = AlertsOverTimeData, - TContext = SiemContext - > = Resolver; - export type TotalCountResolver< - R = number, - Parent = AlertsOverTimeData, - TContext = SiemContext - > = Resolver; -} - -export namespace InspectResolvers { - export interface Resolvers { - dsl?: DslResolver; - - response?: ResponseResolver; - } - - export type DslResolver = Resolver< - R, - Parent, - TContext - >; - export type ResponseResolver = Resolver< - R, - Parent, - TContext - >; -} - -export namespace MatrixOverTimeHistogramDataResolvers { - export interface Resolvers { - x?: XResolver; - - y?: YResolver; - - g?: GResolver; - } - - export type XResolver< - R = number, - Parent = MatrixOverTimeHistogramData, - TContext = SiemContext - > = Resolver; - export type YResolver< - R = number, - Parent = MatrixOverTimeHistogramData, - TContext = SiemContext - > = Resolver; - export type GResolver< - R = string, - Parent = MatrixOverTimeHistogramData, - TContext = SiemContext - > = Resolver; -} - -export namespace AnomaliesOverTimeDataResolvers { - export interface Resolvers { - inspect?: InspectResolver, TypeParent, TContext>; - - matrixHistogramData?: MatrixHistogramDataResolver< - MatrixOverTimeHistogramData[], - TypeParent, - TContext - >; - - totalCount?: TotalCountResolver; - } - - export type InspectResolver< - R = Maybe, - Parent = AnomaliesOverTimeData, - TContext = SiemContext - > = Resolver; - export type MatrixHistogramDataResolver< - R = MatrixOverTimeHistogramData[], - Parent = AnomaliesOverTimeData, - TContext = SiemContext - > = Resolver; - export type TotalCountResolver< - R = number, - Parent = AnomaliesOverTimeData, - TContext = SiemContext - > = Resolver; -} - export namespace AuthenticationsDataResolvers { export interface Resolvers { edges?: EdgesResolver; @@ -4129,34 +3924,23 @@ export namespace PageInfoPaginatedResolvers { > = Resolver; } -export namespace AuthenticationsOverTimeDataResolvers { - export interface Resolvers { - inspect?: InspectResolver, TypeParent, TContext>; - - matrixHistogramData?: MatrixHistogramDataResolver< - MatrixOverTimeHistogramData[], - TypeParent, - TContext - >; +export namespace InspectResolvers { + export interface Resolvers { + dsl?: DslResolver; - totalCount?: TotalCountResolver; + response?: ResponseResolver; } - export type InspectResolver< - R = Maybe, - Parent = AuthenticationsOverTimeData, - TContext = SiemContext - > = Resolver; - export type MatrixHistogramDataResolver< - R = MatrixOverTimeHistogramData[], - Parent = AuthenticationsOverTimeData, - TContext = SiemContext - > = Resolver; - export type TotalCountResolver< - R = number, - Parent = AuthenticationsOverTimeData, - TContext = SiemContext - > = Resolver; + export type DslResolver = Resolver< + R, + Parent, + TContext + >; + export type ResponseResolver = Resolver< + R, + Parent, + TContext + >; } export namespace TimelineDataResolvers { @@ -6343,36 +6127,6 @@ export namespace LastEventTimeDataResolvers { > = Resolver; } -export namespace EventsOverTimeDataResolvers { - export interface Resolvers { - inspect?: InspectResolver, TypeParent, TContext>; - - matrixHistogramData?: MatrixHistogramDataResolver< - MatrixOverTimeHistogramData[], - TypeParent, - TContext - >; - - totalCount?: TotalCountResolver; - } - - export type InspectResolver< - R = Maybe, - Parent = EventsOverTimeData, - TContext = SiemContext - > = Resolver; - export type MatrixHistogramDataResolver< - R = MatrixOverTimeHistogramData[], - Parent = EventsOverTimeData, - TContext = SiemContext - > = Resolver; - export type TotalCountResolver< - R = number, - Parent = EventsOverTimeData, - TContext = SiemContext - > = Resolver; -} - export namespace HostsDataResolvers { export interface Resolvers { edges?: EdgesResolver; @@ -7077,6 +6831,62 @@ export namespace KpiHostDetailsDataResolvers { > = Resolver; } +export namespace MatrixHistogramOverTimeDataResolvers { + export interface Resolvers { + inspect?: InspectResolver, TypeParent, TContext>; + + matrixHistogramData?: MatrixHistogramDataResolver< + MatrixOverTimeHistogramData[], + TypeParent, + TContext + >; + + totalCount?: TotalCountResolver; + } + + export type InspectResolver< + R = Maybe, + Parent = MatrixHistogramOverTimeData, + TContext = SiemContext + > = Resolver; + export type MatrixHistogramDataResolver< + R = MatrixOverTimeHistogramData[], + Parent = MatrixHistogramOverTimeData, + TContext = SiemContext + > = Resolver; + export type TotalCountResolver< + R = number, + Parent = MatrixHistogramOverTimeData, + TContext = SiemContext + > = Resolver; +} + +export namespace MatrixOverTimeHistogramDataResolvers { + export interface Resolvers { + x?: XResolver, TypeParent, TContext>; + + y?: YResolver, TypeParent, TContext>; + + g?: GResolver, TypeParent, TContext>; + } + + export type XResolver< + R = Maybe, + Parent = MatrixOverTimeHistogramData, + TContext = SiemContext + > = Resolver; + export type YResolver< + R = Maybe, + Parent = MatrixOverTimeHistogramData, + TContext = SiemContext + > = Resolver; + export type GResolver< + R = Maybe, + Parent = MatrixOverTimeHistogramData, + TContext = SiemContext + > = Resolver; +} + export namespace NetworkTopCountriesDataResolvers { export interface Resolvers { edges?: EdgesResolver; @@ -9224,10 +9034,6 @@ export type IResolvers = { SourceFields?: SourceFieldsResolvers.Resolvers; SourceStatus?: SourceStatusResolvers.Resolvers; IndexField?: IndexFieldResolvers.Resolvers; - AlertsOverTimeData?: AlertsOverTimeDataResolvers.Resolvers; - Inspect?: InspectResolvers.Resolvers; - MatrixOverTimeHistogramData?: MatrixOverTimeHistogramDataResolvers.Resolvers; - AnomaliesOverTimeData?: AnomaliesOverTimeDataResolvers.Resolvers; AuthenticationsData?: AuthenticationsDataResolvers.Resolvers; AuthenticationsEdges?: AuthenticationsEdgesResolvers.Resolvers; AuthenticationItem?: AuthenticationItemResolvers.Resolvers; @@ -9240,7 +9046,7 @@ export type IResolvers = { OsEcsFields?: OsEcsFieldsResolvers.Resolvers; CursorType?: CursorTypeResolvers.Resolvers; PageInfoPaginated?: PageInfoPaginatedResolvers.Resolvers; - AuthenticationsOverTimeData?: AuthenticationsOverTimeDataResolvers.Resolvers; + Inspect?: InspectResolvers.Resolvers; TimelineData?: TimelineDataResolvers.Resolvers; TimelineEdges?: TimelineEdgesResolvers.Resolvers; TimelineItem?: TimelineItemResolvers.Resolvers; @@ -9294,7 +9100,6 @@ export type IResolvers = { TimelineDetailsData?: TimelineDetailsDataResolvers.Resolvers; DetailItem?: DetailItemResolvers.Resolvers; LastEventTimeData?: LastEventTimeDataResolvers.Resolvers; - EventsOverTimeData?: EventsOverTimeDataResolvers.Resolvers; HostsData?: HostsDataResolvers.Resolvers; HostsEdges?: HostsEdgesResolvers.Resolvers; HostItem?: HostItemResolvers.Resolvers; @@ -9315,6 +9120,8 @@ export type IResolvers = { KpiHostsData?: KpiHostsDataResolvers.Resolvers; KpiHostHistogramData?: KpiHostHistogramDataResolvers.Resolvers; KpiHostDetailsData?: KpiHostDetailsDataResolvers.Resolvers; + MatrixHistogramOverTimeData?: MatrixHistogramOverTimeDataResolvers.Resolvers; + MatrixOverTimeHistogramData?: MatrixOverTimeHistogramDataResolvers.Resolvers; NetworkTopCountriesData?: NetworkTopCountriesDataResolvers.Resolvers; NetworkTopCountriesEdges?: NetworkTopCountriesEdgesResolvers.Resolvers; NetworkTopCountriesItem?: NetworkTopCountriesItemResolvers.Resolvers; diff --git a/x-pack/legacy/plugins/siem/server/init_server.ts b/x-pack/legacy/plugins/siem/server/init_server.ts index 1f4f1b176497fd..6158a33c25cfa2 100644 --- a/x-pack/legacy/plugins/siem/server/init_server.ts +++ b/x-pack/legacy/plugins/siem/server/init_server.ts @@ -6,7 +6,6 @@ import { IResolvers, makeExecutableSchema } from 'graphql-tools'; import { schemas } from './graphql'; -import { createAnomaliesResolvers } from './graphql/anomalies'; import { createAuthenticationsResolvers } from './graphql/authentications'; import { createScalarToStringArrayValueResolvers } from './graphql/ecs'; import { createEsValueResolvers, createEventsResolvers } from './graphql/events'; @@ -30,19 +29,18 @@ import { createUncommonProcessesResolvers } from './graphql/uncommon_processes'; import { createWhoAmIResolvers } from './graphql/who_am_i'; import { AppBackendLibs } from './lib/types'; import { createTlsResolvers } from './graphql/tls'; -import { createAlertsResolvers } from './graphql/alerts'; +import { createMatrixHistogramResolvers } from './graphql/matrix_histogram'; export const initServer = (libs: AppBackendLibs) => { const schema = makeExecutableSchema({ resolvers: [ - createAlertsResolvers(libs) as IResolvers, - createAnomaliesResolvers(libs) as IResolvers, createAuthenticationsResolvers(libs) as IResolvers, createEsValueResolvers() as IResolvers, createEventsResolvers(libs) as IResolvers, createHostsResolvers(libs) as IResolvers, createIpDetailsResolvers(libs) as IResolvers, createKpiNetworkResolvers(libs) as IResolvers, + createMatrixHistogramResolvers(libs) as IResolvers, createNoteResolvers(libs) as IResolvers, createPinnedEventResolvers(libs) as IResolvers, createSourcesResolvers(libs) as IResolvers, diff --git a/x-pack/legacy/plugins/siem/server/lib/alerts/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/alerts/elasticsearch_adapter.ts deleted file mode 100644 index cedd7815968120..00000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/alerts/elasticsearch_adapter.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 { get, getOr } from 'lodash/fp'; - -import { AlertsOverTimeData, MatrixOverTimeHistogramData } from '../../graphql/types'; - -import { inspectStringifyObject } from '../../utils/build_query'; - -import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; -import { buildAlertsHistogramQuery } from './query.dsl'; - -import { AlertsAdapter, AlertsGroupData, AlertsBucket } from './types'; -import { TermAggregation } from '../types'; -import { EventHit } from '../events/types'; - -export class ElasticsearchAlertsAdapter implements AlertsAdapter { - constructor(private readonly framework: FrameworkAdapter) {} - - public async getAlertsHistogramData( - request: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - const dsl = buildAlertsHistogramQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - const totalCount = getOr(0, 'hits.total.value', response); - const matrixHistogramData = getOr([], 'aggregations.alertsByModuleGroup.buckets', response); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - return { - inspect, - matrixHistogramData: getAlertsOverTimeByModule(matrixHistogramData), - totalCount, - }; - } -} - -const getAlertsOverTimeByModule = (data: AlertsGroupData[]): MatrixOverTimeHistogramData[] => { - let result: MatrixOverTimeHistogramData[] = []; - data.forEach(({ key: group, alerts }) => { - const alertsData: AlertsBucket[] = get('buckets', alerts); - - result = [ - ...result, - ...alertsData.map(({ key, doc_count }: AlertsBucket) => ({ - x: key, - y: doc_count, - g: group, - })), - ]; - }); - - return result; -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/alerts/types.ts b/x-pack/legacy/plugins/siem/server/lib/alerts/types.ts deleted file mode 100644 index 67da38e8052d29..00000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/alerts/types.ts +++ /dev/null @@ -1,27 +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 { AlertsOverTimeData } from '../../graphql/types'; -import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; - -export interface AlertsBucket { - key: number; - doc_count: number; -} - -export interface AlertsGroupData { - key: string; - doc_count: number; - alerts: { - buckets: AlertsBucket[]; - }; -} -export interface AlertsAdapter { - getAlertsHistogramData( - request: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise; -} diff --git a/x-pack/legacy/plugins/siem/server/lib/anomalies/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/anomalies/elasticsearch_adapter.ts deleted file mode 100644 index 0955bc69c7c939..00000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/anomalies/elasticsearch_adapter.ts +++ /dev/null @@ -1,64 +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 { getOr } from 'lodash/fp'; - -import { AnomaliesOverTimeData } from '../../graphql/types'; -import { inspectStringifyObject } from '../../utils/build_query'; -import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; -import { TermAggregation } from '../types'; - -import { AnomalyHit, AnomaliesAdapter, AnomaliesActionGroupData } from './types'; -import { buildAnomaliesOverTimeQuery } from './query.anomalies_over_time.dsl'; -import { MatrixOverTimeHistogramData } from '../../../public/graphql/types'; - -export class ElasticsearchAnomaliesAdapter implements AnomaliesAdapter { - constructor(private readonly framework: FrameworkAdapter) {} - - public async getAnomaliesOverTime( - request: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - const dsl = buildAnomaliesOverTimeQuery(options); - - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - - const totalCount = getOr(0, 'hits.total.value', response); - const anomaliesOverTimeBucket = getOr([], 'aggregations.anomalyActionGroup.buckets', response); - - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - return { - inspect, - matrixHistogramData: getAnomaliesOverTimeByJobId(anomaliesOverTimeBucket), - totalCount, - }; - } -} - -const getAnomaliesOverTimeByJobId = ( - data: AnomaliesActionGroupData[] -): MatrixOverTimeHistogramData[] => { - let result: MatrixOverTimeHistogramData[] = []; - data.forEach(({ key: group, anomalies }) => { - const anomaliesData = getOr([], 'buckets', anomalies).map( - ({ key, doc_count }: { key: number; doc_count: number }) => ({ - x: key, - y: doc_count, - g: group, - }) - ); - result = [...result, ...anomaliesData]; - }); - - return result; -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/anomalies/index.ts b/x-pack/legacy/plugins/siem/server/lib/anomalies/index.ts deleted file mode 100644 index 727c45a3bac44e..00000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/anomalies/index.ts +++ /dev/null @@ -1,21 +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 { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; -export * from './elasticsearch_adapter'; -import { AnomaliesAdapter } from './types'; -import { AnomaliesOverTimeData } from '../../../public/graphql/types'; - -export class Anomalies { - constructor(private readonly adapter: AnomaliesAdapter) {} - - public async getAnomaliesOverTime( - req: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - return this.adapter.getAnomaliesOverTime(req, options); - } -} diff --git a/x-pack/legacy/plugins/siem/server/lib/anomalies/types.ts b/x-pack/legacy/plugins/siem/server/lib/anomalies/types.ts deleted file mode 100644 index 9fde81da63ec79..00000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/anomalies/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { AnomaliesOverTimeData } from '../../graphql/types'; -import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; -import { SearchHit } from '../types'; - -export interface AnomaliesAdapter { - getAnomaliesOverTime( - req: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise; -} - -export interface AnomalySource { - [field: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any -} - -export interface AnomalyHit extends SearchHit { - sort: string[]; - _source: AnomalySource; - aggregations: { - [agg: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any - }; -} - -interface AnomaliesOverTimeHistogramData { - key_as_string: string; - key: number; - doc_count: number; -} - -export interface AnomaliesActionGroupData { - key: number; - anomalies: { - bucket: AnomaliesOverTimeHistogramData[]; - }; - doc_count: number; -} diff --git a/x-pack/legacy/plugins/siem/server/lib/authentications/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/authentications/elasticsearch_adapter.ts index 85008adcd985f8..79f13ce4461e5a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/authentications/elasticsearch_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/authentications/elasticsearch_adapter.ts @@ -6,50 +6,20 @@ import { getOr } from 'lodash/fp'; -import { - AuthenticationsData, - AuthenticationsEdges, - AuthenticationsOverTimeData, - MatrixOverTimeHistogramData, -} from '../../graphql/types'; +import { AuthenticationsData, AuthenticationsEdges } from '../../graphql/types'; import { mergeFieldsWithHit, inspectStringifyObject } from '../../utils/build_query'; -import { - FrameworkAdapter, - FrameworkRequest, - RequestOptionsPaginated, - MatrixHistogramRequestOptions, -} from '../framework'; +import { FrameworkAdapter, FrameworkRequest, RequestOptionsPaginated } from '../framework'; import { TermAggregation } from '../types'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants'; import { auditdFieldsMap, buildQuery } from './query.dsl'; -import { buildAuthenticationsOverTimeQuery } from './query.authentications_over_time.dsl'; import { AuthenticationBucket, AuthenticationData, AuthenticationHit, AuthenticationsAdapter, - AuthenticationsActionGroupData, } from './types'; -const getAuthenticationsOverTimeByAuthenticationResult = ( - data: AuthenticationsActionGroupData[] -): MatrixOverTimeHistogramData[] => { - let result: MatrixOverTimeHistogramData[] = []; - data.forEach(({ key: group, events }) => { - const eventsData = getOr([], 'buckets', events).map( - ({ key, doc_count }: { key: number; doc_count: number }) => ({ - x: key, - y: doc_count, - g: group, - }) - ); - result = [...result, ...eventsData]; - }); - - return result; -}; - export class ElasticsearchAuthenticationAdapter implements AuthenticationsAdapter { constructor(private readonly framework: FrameworkAdapter) {} @@ -109,35 +79,6 @@ export class ElasticsearchAuthenticationAdapter implements AuthenticationsAdapte }, }; } - - public async getAuthenticationsOverTime( - request: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - const dsl = buildAuthenticationsOverTimeQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - const totalCount = getOr(0, 'hits.total.value', response); - const authenticationsOverTimeBucket = getOr( - [], - 'aggregations.eventActionGroup.buckets', - response - ); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - return { - inspect, - matrixHistogramData: getAuthenticationsOverTimeByAuthenticationResult( - authenticationsOverTimeBucket - ), - totalCount, - }; - } } export const formatAuthenticationData = ( diff --git a/x-pack/legacy/plugins/siem/server/lib/authentications/index.ts b/x-pack/legacy/plugins/siem/server/lib/authentications/index.ts index bd5712c105f31d..c1b93818943dbe 100644 --- a/x-pack/legacy/plugins/siem/server/lib/authentications/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/authentications/index.ts @@ -5,14 +5,9 @@ */ import { AuthenticationsData } from '../../graphql/types'; -import { - FrameworkRequest, - RequestOptionsPaginated, - MatrixHistogramRequestOptions, -} from '../framework'; +import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; import { AuthenticationsAdapter } from './types'; -import { AuthenticationsOverTimeData } from '../../../public/graphql/types'; export class Authentications { constructor(private readonly adapter: AuthenticationsAdapter) {} @@ -23,11 +18,4 @@ export class Authentications { ): Promise { return this.adapter.getAuthentications(req, options); } - - public async getAuthenticationsOverTime( - req: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - return this.adapter.getAuthenticationsOverTime(req, options); - } } diff --git a/x-pack/legacy/plugins/siem/server/lib/authentications/types.ts b/x-pack/legacy/plugins/siem/server/lib/authentications/types.ts index e1ec871ff4b589..2d2c7ba547c094 100644 --- a/x-pack/legacy/plugins/siem/server/lib/authentications/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/authentications/types.ts @@ -4,16 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - AuthenticationsData, - AuthenticationsOverTimeData, - LastSourceHost, -} from '../../graphql/types'; -import { - FrameworkRequest, - RequestOptionsPaginated, - MatrixHistogramRequestOptions, -} from '../framework'; +import { AuthenticationsData, LastSourceHost } from '../../graphql/types'; +import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; import { Hit, SearchHit, TotalHit } from '../types'; export interface AuthenticationsAdapter { @@ -21,10 +13,6 @@ export interface AuthenticationsAdapter { req: FrameworkRequest, options: RequestOptionsPaginated ): Promise; - getAuthenticationsOverTime( - req: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise; } type StringOrNumber = string | number; @@ -72,17 +60,3 @@ export interface AuthenticationData extends SearchHit { }; }; } - -interface AuthenticationsOverTimeHistogramData { - key_as_string: string; - key: number; - doc_count: number; -} - -export interface AuthenticationsActionGroupData { - key: number; - events: { - bucket: AuthenticationsOverTimeHistogramData[]; - }; - doc_count: number; -} diff --git a/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts b/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts index 0ab6f1a8df779d..9c46f3320e37ec 100644 --- a/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts +++ b/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts @@ -6,8 +6,6 @@ import { CoreSetup, SetupPlugins } from '../../plugin'; -import { Anomalies } from '../anomalies'; -import { ElasticsearchAnomaliesAdapter } from '../anomalies/elasticsearch_adapter'; import { Authentications } from '../authentications'; import { ElasticsearchAuthenticationAdapter } from '../authentications/elasticsearch_adapter'; import { ElasticsearchEventsAdapter, Events } from '../events'; @@ -32,7 +30,7 @@ import { ElasticsearchUncommonProcessesAdapter, UncommonProcesses } from '../unc import { Note } from '../note/saved_object'; import { PinnedEvent } from '../pinned_event/saved_object'; import { Timeline } from '../timeline/saved_object'; -import { Alerts, ElasticsearchAlertsAdapter } from '../alerts'; +import { ElasticsearchMatrixHistogramAdapter, MatrixHistogram } from '../matrix_histogram'; export function compose( core: CoreSetup, @@ -48,8 +46,6 @@ export function compose( const pinnedEvent = new PinnedEvent(); const domainLibs: AppDomainLibs = { - alerts: new Alerts(new ElasticsearchAlertsAdapter(framework)), - anomalies: new Anomalies(new ElasticsearchAnomaliesAdapter(framework)), authentications: new Authentications(new ElasticsearchAuthenticationAdapter(framework)), events: new Events(new ElasticsearchEventsAdapter(framework)), fields: new IndexFields(new ElasticsearchIndexFieldAdapter(framework)), @@ -58,6 +54,7 @@ export function compose( tls: new TLS(new ElasticsearchTlsAdapter(framework)), kpiHosts: new KpiHosts(new ElasticsearchKpiHostsAdapter(framework)), kpiNetwork: new KpiNetwork(new ElasticsearchKpiNetworkAdapter(framework)), + matrixHistogram: new MatrixHistogram(new ElasticsearchMatrixHistogramAdapter(framework)), network: new Network(new ElasticsearchNetworkAdapter(framework)), overview: new Overview(new ElasticsearchOverviewAdapter(framework)), uncommonProcesses: new UncommonProcesses(new ElasticsearchUncommonProcessesAdapter(framework)), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 1578c71dddc6a9..2b50011cf4dff1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -427,13 +427,54 @@ export const getMockPrivileges = () => ({ has_encryption_key: true, }); -export const getFindResultStatus = (): SavedObjectsFindResponse => ({ +export const getFindResultStatusEmpty = (): SavedObjectsFindResponse => ({ page: 1, per_page: 1, total: 0, saved_objects: [], }); +export const getFindResultStatus = (): SavedObjectsFindResponse => ({ + page: 1, + per_page: 6, + total: 2, + saved_objects: [ + { + type: 'my-type', + id: 'e0b86950-4e9f-11ea-bdbd-07b56aa159b3', + attributes: { + alertId: '1ea5a820-4da1-4e82-92a1-2b43a7bece08', + statusDate: '2020-02-18T15:26:49.783Z', + status: 'succeeded', + lastFailureAt: null, + lastSuccessAt: '2020-02-18T15:26:49.783Z', + lastFailureMessage: null, + lastSuccessMessage: 'succeeded', + }, + references: [], + updated_at: '2020-02-18T15:26:51.333Z', + version: 'WzQ2LDFd', + }, + { + type: 'my-type', + id: '91246bd0-5261-11ea-9650-33b954270f67', + attributes: { + alertId: '1ea5a820-4da1-4e82-92a1-2b43a7bece08', + statusDate: '2020-02-18T15:15:58.806Z', + status: 'failed', + lastFailureAt: '2020-02-18T15:15:58.806Z', + lastSuccessAt: '2020-02-13T20:31:59.855Z', + lastFailureMessage: + 'Signal rule name: "Query with a rule id Number 1", id: "1ea5a820-4da1-4e82-92a1-2b43a7bece08", rule_id: "query-rule-id-1" has a time gap of 5 days (412682928ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.', + lastSuccessMessage: 'succeeded', + }, + references: [], + updated_at: '2020-02-18T15:15:58.860Z', + version: 'WzMyLDFd', + }, + ], +}); + export const getIndexName = () => 'index-name'; export const getEmptyIndex = (): { _shards: Partial } => ({ _shards: { total: 0 }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts index f8c8e1f231ffa2..32226e38a1f7f7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -26,6 +26,20 @@ export const getSimpleRule = (ruleId = 'rule-1'): Partial = query: 'user.name: root or user.name: admin', }); +/** + * This is a typical simple rule for testing that is easy for most basic testing + * @param ruleId + */ +export const getSimpleRuleWithId = (id = 'rule-1'): Partial => ({ + name: 'Simple Rule Query', + description: 'Simple Rule Query', + risk_score: 1, + id, + severity: 'high', + type: 'query', + query: 'user.name: root or user.name: admin', +}); + /** * Given an array of rule_id strings this will return a ndjson buffer which is useful * for testing uploads. @@ -51,3 +65,26 @@ export const getSimpleRuleAsMultipartContent = (ruleIds: string[], isNdjson = tr return Buffer.from(resultingPayload); }; + +/** + * Given an array of rule_id strings this will return a ndjson buffer which is useful + * for testing uploads. + * @param count Number of rules to generate + * @param isNdjson Boolean to determine file extension + */ +export const getSimpleRuleAsMultipartContentNoRuleId = (count: number, isNdjson = true): Buffer => { + const arrayOfRules = Array(count).fill(JSON.stringify(getSimpleRuleWithId())); + const stringOfRules = arrayOfRules.join('\r\n'); + + const resultingPayload = + `--${TEST_BOUNDARY}\r\n` + + `Content-Disposition: form-data; name="file"; filename="rules.${ + isNdjson ? 'ndjson' : 'json' + }\r\n` + + 'Content-Type: application/octet-stream\r\n' + + '\r\n' + + `${stringOfRules}\r\n` + + `--${TEST_BOUNDARY}--\r\n`; + + return Buffer.from(resultingPayload); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts index 41be42f7c0fe1a..26a6c790ceef93 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts @@ -36,25 +36,14 @@ export const createReadIndexRoute = ( const indexExists = await getIndexExists(callCluster, index); if (indexExists) { - // head request is used for if you want to get if the index exists - // or not and it will return a content-length: 0 along with either a 200 or 404 - // depending on if the index exists or not. - if (request.method.toLowerCase() === 'head') { - return headers.response().code(200); - } else { - return headers.response({ name: index }).code(200); - } + return headers.response({ name: index }).code(200); } else { - if (request.method.toLowerCase() === 'head') { - return headers.response().code(404); - } else { - return headers - .response({ - message: 'index for this space does not exist', - status_code: 404, - }) - .code(404); - } + return headers + .response({ + message: 'index for this space does not exist', + status_code: 404, + }) + .code(404); } } catch (err) { const error = transformError(err); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index 308ee95a77e204..3c31658c61d6e3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -5,6 +5,7 @@ */ import { readPrivilegesRoute } from './read_privileges_route'; +import * as readPrivileges from '../../privileges/read_privileges'; import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; import { getPrivilegeRequest, getMockPrivileges } from '../__mocks__/request_responses'; @@ -38,5 +39,16 @@ describe('read_privileges', () => { const { payload } = await inject(getPrivilegeRequest()); expect(JSON.parse(payload)).toEqual(getMockPrivileges()); }); + + test('returns 500 when bad response from readPrivileges', async () => { + jest.spyOn(readPrivileges, 'readPrivileges').mockImplementation(() => { + throw new Error('Test error'); + }); + const { payload } = await inject(getPrivilegeRequest()); + expect(JSON.parse(payload)).toEqual({ + message: 'Test error', + status_code: 500, + }); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index e018ed4cc22ff4..e6a93fdadcfcaa 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -6,7 +6,6 @@ import { omit } from 'lodash/fp'; -import { createRulesRoute } from './create_rules_route'; import { getFindResult, getResult, @@ -17,6 +16,7 @@ import { getNonEmptyIndex, } from '../__mocks__/request_responses'; import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; +import * as updatePrepackagedRules from '../../rules/update_prepacked_rules'; jest.mock('../../rules/get_prepackaged_rules', () => { return { @@ -54,7 +54,8 @@ describe('add_prepackaged_rules_route', () => { beforeEach(() => { jest.resetAllMocks(); - + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); config = createMockConfig(); getClients = clientsServiceMock.createGetScoped(); @@ -78,9 +79,7 @@ describe('add_prepackaged_rules_route', () => { test('returns 404 if alertClient is not available on the route', async () => { getClients.mockResolvedValue(omit('alertsClient', clients)); - const { inject, route } = createMockServer(); - createRulesRoute(route, config, getClients); - const { statusCode } = await inject(addPrepackagedRulesRequest()); + const { statusCode } = await server.inject(addPrepackagedRulesRequest()); expect(statusCode).toBe(404); }); }); @@ -126,5 +125,19 @@ describe('add_prepackaged_rules_route', () => { rules_updated: 1, }); }); + test('catches errors if payloads cause errors to be thrown', async () => { + jest.spyOn(updatePrepackagedRules, 'updatePrepackagedRules').mockImplementation(() => { + throw new Error('Test error'); + }); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(addPrepackagedRulesRequest()); + expect(JSON.parse(payload)).toEqual({ + message: 'Test error', + status_code: 500, + }); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index 664d27a7572ad1..931623ea6652c6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -14,12 +14,15 @@ import { typicalPayload, getReadBulkRequest, getEmptyIndex, + getNonEmptyIndex, } from '../__mocks__/request_responses'; import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { createRulesBulkRoute } from './create_rules_bulk_route'; import { BulkError } from '../utils'; import { OutputRuleAlertRest } from '../../types'; +import * as createRules from '../../rules/create_rules'; +import * as readRules from '../../rules/read_rules'; describe('create_rules_bulk', () => { let server = createMockServer(); @@ -29,10 +32,14 @@ describe('create_rules_bulk', () => { beforeEach(() => { jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); config = createMockConfig(); getClients = clientsServiceMock.createGetScoped(); clients = clientsServiceMock.createClients(); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getNonEmptyIndex()); + getClients.mockResolvedValue(clients); createRulesBulkRoute(server.route, config, getClients); @@ -44,8 +51,12 @@ describe('create_rules_bulk', () => { clients.alertsClient.get.mockResolvedValue(getResult()); clients.actionsClient.create.mockResolvedValue(createActionResult()); clients.alertsClient.create.mockResolvedValue(getResult()); - const { statusCode } = await server.inject(getReadBulkRequest()); + jest.spyOn(createRules, 'createRules').mockImplementation(async () => { + return getResult(); + }); + const { payload, statusCode } = await server.inject(getReadBulkRequest()); expect(statusCode).toBe(200); + expect(JSON.parse(payload).error).toBeUndefined(); }); test('returns 404 if alertClient is not available on the route', async () => { @@ -149,6 +160,24 @@ describe('create_rules_bulk', () => { expect(output.some(item => item.error?.status_code === 409)).toBeTruthy(); }); + test('returns 409 if duplicate rule_ids found in rule saved objects', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + jest.spyOn(readRules, 'readRules').mockImplementation(async () => { + return getResult(); + }); + const request: ServerInjectOptions = { + method: 'POST', + url: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + payload: [typicalPayload()], + }; + const { payload } = await server.inject(request); + const output: Array> = JSON.parse(payload); + expect(output.some(item => item.error?.status_code === 409)).toBeTruthy(); + }); + test('returns one error object in response when duplicate rule_ids found in request payload', async () => { clients.alertsClient.find.mockResolvedValue(getFindResult()); clients.alertsClient.get.mockResolvedValue(getResult()); @@ -163,4 +192,22 @@ describe('create_rules_bulk', () => { const output: Array> = JSON.parse(payload); expect(output.length).toBe(1); }); + + test('catches error if createRules throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + jest.spyOn(createRules, 'createRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const request: ServerInjectOptions = { + method: 'POST', + url: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + payload: [typicalPayload()], + }; + const { payload } = await server.inject(request); + const output: Array> = JSON.parse(payload); + expect(output[0].error.message).toBe('Test error'); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index 4f28771db8ed78..5ad43e70f2651e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -9,6 +9,9 @@ import { omit } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { createRulesRoute } from './create_rules_route'; +import * as createRules from '../../rules/create_rules'; +import * as readRules from '../../rules/read_rules'; +import * as utils from './utils'; import { getFindResult, @@ -29,8 +32,12 @@ describe('create_rules', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); - + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); config = createMockConfig(); getClients = clientsServiceMock.createGetScoped(); @@ -130,5 +137,44 @@ describe('create_rules', () => { const { statusCode } = await server.inject(request); expect(statusCode).toBe(400); }); + + test('catches error if createRules throws error', async () => { + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(createRules, 'createRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getCreateRequest()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); + + test('catches error if transform returns null', async () => { + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(utils, 'transform').mockReturnValue(null); + const { payload, statusCode } = await server.inject(getCreateRequest()); + expect(JSON.parse(payload).message).toBe('Internal error transforming rules'); + expect(statusCode).toBe(500); + }); + + test('returns 409 if duplicate rule_ids found in rule saved objects', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + jest.spyOn(readRules, 'readRules').mockImplementation(async () => { + return getResult(); + }); + const { payload } = await server.inject(getCreateRequest()); + const output = JSON.parse(payload); + expect(output.status_code).toEqual(409); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts index 855bf7f634c26b..fb44f96d76859f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts @@ -48,6 +48,16 @@ describe('delete_rules', () => { expect(statusCode).toBe(200); }); + test('resturns 200 when deleting a single rule and related rule status', async () => { + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.savedObjectsClient.delete.mockResolvedValue(true); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + const { statusCode } = await server.inject(getDeleteBulkRequest()); + expect(statusCode).toBe(200); + }); + test('returns 200 when deleting a single rule with a valid actionClient and alertClient by alertId using POST', async () => { clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.alertsClient.get.mockResolvedValue(getResult()); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 6438318cb43dbb..aabf3e513bfea2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -58,7 +58,7 @@ export const createDeleteRulesBulkRoute = (getClients: GetScopedClients): Hapi.S ruleStatuses.saved_objects.forEach(async obj => savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id) ); - return transformOrBulkError(idOrRuleIdOrUnknown, rule); + return transformOrBulkError(idOrRuleIdOrUnknown, rule, ruleStatuses); } else { return getIdBulkError({ id, ruleId }); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index a0a6f61223279b..57c7c859766196 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -7,6 +7,8 @@ import { ServerInjectOptions } from 'hapi'; import { omit } from 'lodash/fp'; import { deleteRulesRoute } from './delete_rules_route'; +import * as utils from './utils'; +import * as deleteRules from '../../rules/delete_rules'; import { getFindResult, @@ -25,7 +27,12 @@ describe('delete_rules', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); getClients = clientsServiceMock.createGetScoped(); clients = clientsServiceMock.createClients(); @@ -73,6 +80,32 @@ describe('delete_rules', () => { const { statusCode } = await inject(getDeleteRequest()); expect(statusCode).toBe(404); }); + + test('returns 500 when transform fails', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.savedObjectsClient.delete.mockResolvedValue({}); + jest.spyOn(utils, 'transform').mockReturnValue(null); + const { payload, statusCode } = await server.inject(getDeleteRequest()); + expect(JSON.parse(payload).message).toBe('Internal error transforming rules'); + expect(statusCode).toBe(500); + }); + + test('catches error if deleteRules throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.savedObjectsClient.delete.mockResolvedValue({}); + jest.spyOn(deleteRules, 'deleteRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getDeleteRequestById()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('validation', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts index 5b75f17164acf5..019424ea2420a6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts @@ -6,13 +6,22 @@ import { omit } from 'lodash/fp'; +import { + getFindResult, + getResult, + getFindResultWithSingleHit, + getFindResultStatus, + getFindRequest, +} from '../__mocks__/request_responses'; import { createMockServer } from '../__mocks__'; import { clientsServiceMock } from '../__mocks__/clients_service_mock'; +import * as utils from './utils'; +import * as findRules from '../../rules/find_rules'; + import { findRulesRoute } from './find_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { getFindResult, getResult, getFindRequest } from '../__mocks__/request_responses'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; describe('find_rules', () => { @@ -21,7 +30,12 @@ describe('find_rules', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); getClients = clientsServiceMock.createGetScoped(); @@ -33,15 +47,10 @@ describe('find_rules', () => { }); describe('status codes with actionClient and alertClient', () => { - test('returns 200 when finding a single rule with a valid actionClient and alertClient', async () => { - clients.alertsClient.find.mockResolvedValue(getFindResult()); - clients.actionsClient.find.mockResolvedValue({ - page: 1, - perPage: 1, - total: 0, - data: [], - }); + test('returns 200 when finding a single rule with a valid alertsClient', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getFindRequest()); expect(statusCode).toBe(200); }); @@ -53,6 +62,28 @@ describe('find_rules', () => { const { statusCode } = await inject(getFindRequest()); expect(statusCode).toBe(404); }); + + test('catches error when transformation fails', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(utils, 'transformFindAlerts').mockReturnValue(null); + const { payload, statusCode } = await server.inject(getFindRequest()); + expect(statusCode).toBe(500); + expect(JSON.parse(payload).message).toBe('unknown data type, error transforming alert'); + }); + + test('catch error when findRules function throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(findRules, 'findRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getFindRequest()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('validation', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts new file mode 100644 index 00000000000000..00411c550fa2ea --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { omit } from 'lodash/fp'; + +import { getFindResultStatus } from '../__mocks__/request_responses'; +import { createMockServer } from '../__mocks__'; +import { clientsServiceMock } from '../__mocks__/clients_service_mock'; + +import { findRulesStatusesRoute } from './find_rules_status_route'; +import { ServerInjectOptions } from 'hapi'; + +import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; + +describe('find_statuses', () => { + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); + + beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + + findRulesStatusesRoute(server.route, getClients); + }); + + describe('status codes with actionClient and alertClient', () => { + test('returns 200 when finding a single rule status with a valid alertsClient', async () => { + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + const request: ServerInjectOptions = { + method: 'GET', + url: `${DETECTION_ENGINE_RULES_URL}/_find_statuses?ids=["someid"]`, + }; + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(200); + }); + + test('returns 404 if alertClient is not available on the route', async () => { + getClients.mockResolvedValue(omit('alertsClient', clients)); + const request: ServerInjectOptions = { + method: 'GET', + url: `${DETECTION_ENGINE_RULES_URL}/_find_statuses?ids=["someid"]`, + }; + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(404); + }); + + test('catch error when savedObjectsClient find function throws error', async () => { + clients.savedObjectsClient.find.mockImplementation(async () => { + throw new Error('Test error'); + }); + const request: ServerInjectOptions = { + method: 'GET', + url: `${DETECTION_ENGINE_RULES_URL}/_find_statuses?ids=["someid"]`, + }; + const { payload, statusCode } = await server.inject(request); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); + }); + + describe('validation', () => { + test('returns 400 if id is given instead of ids', async () => { + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + const request: ServerInjectOptions = { + method: 'GET', + url: `${DETECTION_ENGINE_RULES_URL}/_find_statuses?id=["someid"]`, + }; + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(400); + }); + + test('returns 200 if the set of optional query parameters are given', async () => { + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + const request: ServerInjectOptions = { + method: 'GET', + url: `${DETECTION_ENGINE_RULES_URL}/_find_statuses?ids=["someid"]`, + }; + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(200); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts index fe8742ff0b60cf..c496c7b7ce59cd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts @@ -5,7 +5,6 @@ */ import Hapi from 'hapi'; -import { snakeCase } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { LegacyServices, LegacyRequest } from '../../../../types'; @@ -18,17 +17,7 @@ import { IRuleStatusAttributes, } from '../../rules/types'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const convertToSnakeCase = >(obj: T): Partial | null => { - if (!obj) { - return null; - } - return Object.keys(obj).reduce((acc, item) => { - const newKey = snakeCase(item); - return { ...acc, [newKey]: obj[item] }; - }, {}); -}; +import { transformError, convertToSnakeCase } from '../utils'; export const createFindRulesStatusRoute = (getClients: GetScopedClients): Hapi.ServerRoute => ({ method: 'GET', @@ -57,33 +46,43 @@ export const createFindRulesStatusRoute = (getClients: GetScopedClients): Hapi.S "anotherAlertId": ... } */ - const statuses = await query.ids.reduce>(async (acc, id) => { - const lastFiveErrorsForId = await savedObjectsClient.find< - IRuleSavedAttributesSavedObjectAttributes - >({ - type: ruleStatusSavedObjectType, - perPage: 6, - sortField: 'statusDate', - sortOrder: 'desc', - search: id, - searchFields: ['alertId'], - }); - const accumulated = await acc; - const currentStatus = convertToSnakeCase( - lastFiveErrorsForId.saved_objects[0]?.attributes - ); - const failures = lastFiveErrorsForId.saved_objects - .slice(1) - .map(errorItem => convertToSnakeCase(errorItem.attributes)); - return { - ...accumulated, - [id]: { - current_status: currentStatus, - failures, - }, - }; - }, Promise.resolve({})); - return statuses; + try { + const statuses = await query.ids.reduce>(async (acc, id) => { + const lastFiveErrorsForId = await savedObjectsClient.find< + IRuleSavedAttributesSavedObjectAttributes + >({ + type: ruleStatusSavedObjectType, + perPage: 6, + sortField: 'statusDate', + sortOrder: 'desc', + search: id, + searchFields: ['alertId'], + }); + const accumulated = await acc; + const currentStatus = convertToSnakeCase( + lastFiveErrorsForId.saved_objects[0]?.attributes + ); + const failures = lastFiveErrorsForId.saved_objects + .slice(1) + .map(errorItem => convertToSnakeCase(errorItem.attributes)); + return { + ...accumulated, + [id]: { + current_status: currentStatus, + failures, + }, + }; + }, Promise.resolve({})); + return statuses; + } catch (err) { + const error = transformError(err); + return headers + .response({ + message: error.message, + status_code: error.statusCode, + }) + .code(error.statusCode); + } }, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts index 8f27910a7e5e2b..99157b4d15360d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts @@ -7,6 +7,7 @@ import { omit } from 'lodash/fp'; import { getPrepackagedRulesStatusRoute } from './get_prepackaged_rules_status_route'; +import * as findRules from '../../rules/find_rules'; import { getFindResult, @@ -47,7 +48,12 @@ describe('get_prepackaged_rule_status_route', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); getClients = clientsServiceMock.createGetScoped(); @@ -76,6 +82,17 @@ describe('get_prepackaged_rule_status_route', () => { const { statusCode } = await inject(getPrepackagedRulesStatusRequest()); expect(statusCode).toBe(404); }); + + test('catch error when findRules function throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + jest.spyOn(findRules, 'findRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getPrepackagedRulesStatusRequest()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('payload', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index b1dd08f8ca371b..c8b77e505b5d7a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -8,6 +8,7 @@ import { omit } from 'lodash/fp'; import { getSimpleRuleAsMultipartContent, + getSimpleRuleAsMultipartContentNoRuleId, TEST_BOUNDARY, UNPARSABLE_LINE, getSimpleRule, @@ -25,6 +26,7 @@ import { import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; import { importRulesRoute } from './import_rules_route'; import { DEFAULT_SIGNALS_INDEX } from '../../../../../common/constants'; +import * as createRulesStreamFromNdJson from '../../rules/create_rules_stream_from_ndjson'; describe('import_rules_route', () => { let server = createMockServer(); @@ -33,8 +35,12 @@ describe('import_rules_route', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); - + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); config = createMockConfig(); config = () => ({ @@ -94,6 +100,21 @@ describe('import_rules_route', () => { const { statusCode } = await inject(getImportRulesRequest(requestPayload)); expect(statusCode).toEqual(404); }); + + test('returns error if createPromiseFromStreams throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + jest + .spyOn(createRulesStreamFromNdJson, 'createRulesStreamFromNdJson') + .mockImplementation(() => { + throw new Error('Test error'); + }); + const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']); + const { payload, statusCode } = await server.inject(getImportRulesRequest(requestPayload)); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('validation', () => { @@ -306,6 +327,21 @@ describe('import_rules_route', () => { expect(statusCode).toEqual(200); }); + test('returns 200 with errors if all rules are missing rule_ids and import fails on validation', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResult()); + + const requestPayload = getSimpleRuleAsMultipartContentNoRuleId(2); + const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload)); + const parsed: ImportSuccessError = JSON.parse(payload); + + expect(parsed.success).toEqual(false); + expect(parsed.errors[0].error.message).toEqual( + 'child "rule_id" fails because ["rule_id" is required]' + ); + expect(parsed.errors[0].error.status_code).toEqual(400); + expect(statusCode).toEqual(200); + }); + test('returns 200 with reported conflict if error parsing rule', async () => { const multipartPayload = `--${TEST_BOUNDARY}\r\n` + diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index f438e0120f96ac..a9358a47f25fca 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -7,6 +7,7 @@ import Hapi from 'hapi'; import { chunk, isEmpty } from 'lodash/fp'; import { extname } from 'path'; +import { Readable } from 'stream'; import { createPromiseFromStreams } from '../../../../../../../../../src/legacy/utils/streams'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; @@ -15,7 +16,7 @@ import { createRules } from '../../rules/create_rules'; import { ImportRulesRequest } from '../../rules/types'; import { readRules } from '../../rules/read_rules'; import { getIndexExists } from '../../index/get_index_exists'; -import { getIndex, createBulkErrorObject, ImportRuleResponse } from '../utils'; +import { getIndex, transformError, createBulkErrorObject, ImportRuleResponse } from '../utils'; import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson'; import { ImportRuleAlertRest } from '../../types'; import { patchRules } from '../../rules/patch_rules'; @@ -74,179 +75,192 @@ export const createImportRulesRoute = ( } const objectLimit = config().get('savedObjects.maxImportExportSize'); - const readStream = createRulesStreamFromNdJson(request.payload.file, objectLimit); - const parsedObjects = await createPromiseFromStreams([readStream]); - const [duplicateIdErrors, uniqueParsedObjects] = getTupleDuplicateErrorsAndUniqueRules( - parsedObjects, - request.query.overwrite - ); + try { + const readStream = createRulesStreamFromNdJson(objectLimit); + const parsedObjects = await createPromiseFromStreams([ + request.payload.file as Readable, + ...readStream, + ]); + const [duplicateIdErrors, uniqueParsedObjects] = getTupleDuplicateErrorsAndUniqueRules( + parsedObjects, + request.query.overwrite + ); - const chunkParseObjects = chunk(CHUNK_PARSED_OBJECT_SIZE, uniqueParsedObjects); - let importRuleResponse: ImportRuleResponse[] = []; + const chunkParseObjects = chunk(CHUNK_PARSED_OBJECT_SIZE, uniqueParsedObjects); + let importRuleResponse: ImportRuleResponse[] = []; - while (chunkParseObjects.length) { - const batchParseObjects = chunkParseObjects.shift() ?? []; - const newImportRuleResponse = await Promise.all( - batchParseObjects.reduce>>((accum, parsedRule) => { - const importsWorkerPromise = new Promise( - async (resolve, reject) => { - if (parsedRule instanceof Error) { - // If the JSON object had a validation or parse error then we return - // early with the error and an (unknown) for the ruleId - resolve( - createBulkErrorObject({ - statusCode: 400, - message: parsedRule.message, - }) - ); - return null; - } - const { - description, - enabled, - false_positives: falsePositives, - from, - immutable, - query, - language, - output_index: outputIndex, - saved_id: savedId, - meta, - filters, - rule_id: ruleId, - index, - interval, - max_signals: maxSignals, - risk_score: riskScore, - name, - severity, - tags, - threat, - to, - type, - references, - timeline_id: timelineId, - timeline_title: timelineTitle, - version, - } = parsedRule; - try { - const finalIndex = getIndex(spacesClient.getSpaceId, config); - const indexExists = await getIndexExists( - clusterClient.callAsCurrentUser, - finalIndex - ); - if (!indexExists) { + while (chunkParseObjects.length) { + const batchParseObjects = chunkParseObjects.shift() ?? []; + const newImportRuleResponse = await Promise.all( + batchParseObjects.reduce>>((accum, parsedRule) => { + const importsWorkerPromise = new Promise( + async (resolve, reject) => { + if (parsedRule instanceof Error) { + // If the JSON object had a validation or parse error then we return + // early with the error and an (unknown) for the ruleId resolve( createBulkErrorObject({ - ruleId, - statusCode: 409, - message: `To create a rule, the index must exist first. Index ${finalIndex} does not exist`, + statusCode: 400, + message: parsedRule.message, }) ); + return null; } - const rule = await readRules({ alertsClient, ruleId }); - if (rule == null) { - await createRules({ - alertsClient, - actionsClient, - description, - enabled, - falsePositives, - from, - immutable, - query, - language, - outputIndex: finalIndex, - savedId, - timelineId, - timelineTitle, - meta, - filters, - ruleId, - index, - interval, - maxSignals, - riskScore, - name, - severity, - tags, - to, - type, - threat, - references, - version, - }); - resolve({ rule_id: ruleId, status_code: 200 }); - } else if (rule != null && request.query.overwrite) { - await patchRules({ - alertsClient, - actionsClient, - savedObjectsClient, - description, - enabled, - falsePositives, - from, - immutable, - query, - language, - outputIndex, - savedId, - timelineId, - timelineTitle, - meta, - filters, - id: undefined, - ruleId, - index, - interval, - maxSignals, - riskScore, - name, - severity, - tags, - to, - type, - threat, - references, - version, - }); - resolve({ rule_id: ruleId, status_code: 200 }); - } else if (rule != null) { + const { + description, + enabled, + false_positives: falsePositives, + from, + immutable, + query, + language, + output_index: outputIndex, + saved_id: savedId, + meta, + filters, + rule_id: ruleId, + index, + interval, + max_signals: maxSignals, + risk_score: riskScore, + name, + severity, + tags, + threat, + to, + type, + references, + timeline_id: timelineId, + timeline_title: timelineTitle, + version, + } = parsedRule; + try { + const finalIndex = getIndex(spacesClient.getSpaceId, config); + const indexExists = await getIndexExists( + clusterClient.callAsCurrentUser, + finalIndex + ); + if (!indexExists) { + resolve( + createBulkErrorObject({ + ruleId, + statusCode: 409, + message: `To create a rule, the index must exist first. Index ${finalIndex} does not exist`, + }) + ); + } + const rule = await readRules({ alertsClient, ruleId }); + if (rule == null) { + await createRules({ + alertsClient, + actionsClient, + description, + enabled, + falsePositives, + from, + immutable, + query, + language, + outputIndex: finalIndex, + savedId, + timelineId, + timelineTitle, + meta, + filters, + ruleId, + index, + interval, + maxSignals, + riskScore, + name, + severity, + tags, + to, + type, + threat, + references, + version, + }); + resolve({ rule_id: ruleId, status_code: 200 }); + } else if (rule != null && request.query.overwrite) { + await patchRules({ + alertsClient, + actionsClient, + savedObjectsClient, + description, + enabled, + falsePositives, + from, + immutable, + query, + language, + outputIndex, + savedId, + timelineId, + timelineTitle, + meta, + filters, + id: undefined, + ruleId, + index, + interval, + maxSignals, + riskScore, + name, + severity, + tags, + to, + type, + threat, + references, + version, + }); + resolve({ rule_id: ruleId, status_code: 200 }); + } else if (rule != null) { + resolve( + createBulkErrorObject({ + ruleId, + statusCode: 409, + message: `rule_id: "${ruleId}" already exists`, + }) + ); + } + } catch (err) { resolve( createBulkErrorObject({ ruleId, - statusCode: 409, - message: `rule_id: "${ruleId}" already exists`, + statusCode: 400, + message: err.message, }) ); } - } catch (err) { - resolve( - createBulkErrorObject({ - ruleId, - statusCode: 400, - message: err.message, - }) - ); } - } - ); - return [...accum, importsWorkerPromise]; - }, []) - ); - importRuleResponse = [ - ...duplicateIdErrors, - ...importRuleResponse, - ...newImportRuleResponse, - ]; - } + ); + return [...accum, importsWorkerPromise]; + }, []) + ); + importRuleResponse = [ + ...duplicateIdErrors, + ...importRuleResponse, + ...newImportRuleResponse, + ]; + } - const errorsResp = importRuleResponse.filter(resp => !isEmpty(resp.error)); - return { - success: errorsResp.length === 0, - success_count: importRuleResponse.filter(resp => resp.status_code === 200).length, - errors: errorsResp, - }; + const errorsResp = importRuleResponse.filter(resp => !isEmpty(resp.error)); + return { + success: errorsResp.length === 0, + success_count: importRuleResponse.filter(resp => resp.status_code === 200).length, + errors: errorsResp, + }; + } catch (exc) { + const error = transformError(exc); + return headers + .response({ + message: error.message, + status_code: error.statusCode, + }) + .code(error.statusCode); + } }, }; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts index 02af4135b534fa..1cfb4ae81ab852 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts @@ -15,6 +15,7 @@ import { typicalPayload, getFindResultWithSingleHit, getPatchBulkRequest, + getFindResultStatus, } from '../__mocks__/request_responses'; import { createMockServer, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; @@ -43,6 +44,7 @@ describe('patch_rules_bulk', () => { clients.alertsClient.get.mockResolvedValue(getResult()); clients.actionsClient.update.mockResolvedValue(updateActionResult()); clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getPatchBulkRequest()); expect(statusCode).toBe(200); }); @@ -56,6 +58,13 @@ describe('patch_rules_bulk', () => { expect(statusCode).toBe(200); }); + test('returns 404 as a response when missing alertsClient', async () => { + getClients.mockResolvedValue(omit('alertsClient', clients)); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + const { statusCode } = await server.inject(getPatchBulkRequest()); + expect(statusCode).toBe(404); + }); + test('returns 404 within the payload when updating a single rule that does not exist', async () => { clients.alertsClient.find.mockResolvedValue(getFindResult()); clients.alertsClient.get.mockResolvedValue(getResult()); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index cc84b08fdef119..04cd3a026562f1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -7,6 +7,8 @@ import { ServerInjectOptions } from 'hapi'; import { omit } from 'lodash/fp'; import { patchRulesRoute } from './patch_rules_route'; +import * as utils from './utils'; +import * as patchRules from '../../rules/patch_rules'; import { getFindResult, @@ -26,7 +28,13 @@ describe('patch_rules', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + server = createMockServer(); getClients = clientsServiceMock.createGetScoped(); clients = clientsServiceMock.createClients(); @@ -63,6 +71,32 @@ describe('patch_rules', () => { const { statusCode } = await inject(getPatchRequest()); expect(statusCode).toBe(404); }); + + test('returns 500 when transform fails', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(utils, 'transform').mockReturnValue(null); + const { payload, statusCode } = await server.inject(getPatchRequest()); + expect(JSON.parse(payload).message).toBe('Internal error transforming rules'); + expect(statusCode).toBe(500); + }); + + test('catches error if patchRules throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(patchRules, 'patchRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getPatchRequest()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('validation', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts index 7c4653af97f214..0366d3648e1ea3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts @@ -7,6 +7,9 @@ import { ServerInjectOptions } from 'hapi'; import { omit } from 'lodash/fp'; +import * as utils from './utils'; +import * as readRules from '../../rules/read_rules'; + import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { readRulesRoute } from './read_rules_route'; import { @@ -24,8 +27,12 @@ describe('read_signals', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); - + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); getClients = clientsServiceMock.createGetScoped(); clients = clientsServiceMock.createClients(); @@ -50,6 +57,38 @@ describe('read_signals', () => { const { statusCode } = await inject(getReadRequest()); expect(statusCode).toBe(404); }); + + test('returns error if readRules returns null', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(readRules, 'readRules').mockResolvedValue(null); + const { payload, statusCode } = await server.inject(getReadRequest()); + expect(JSON.parse(payload).message).toBe('rule_id: "rule-1" not found'); + expect(statusCode).toBe(404); + }); + + test('returns 500 when transform fails', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(utils, 'transform').mockReturnValue(null); + const { payload, statusCode } = await server.inject(getReadRequest()); + expect(JSON.parse(payload).message).toBe('Internal error transforming rules'); + expect(statusCode).toBe(500); + }); + + test('catches error if readRules throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(readRules, 'readRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getReadRequest()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('validation', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts index 9ff7ebc37aab12..32a633799ad441 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts @@ -75,9 +75,9 @@ describe('update_rules_bulk', () => { test('returns 404 if alertClient is not available on the route', async () => { getClients.mockResolvedValue(omit('alertsClient', clients)); - const { route, inject } = createMockServer(); + const { route } = createMockServer(); updateRulesRoute(route, config, getClients); - const { statusCode } = await inject(getUpdateBulkRequest()); + const { statusCode } = await server.inject(getUpdateBulkRequest()); expect(statusCode).toBe(404); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 7cadfa94467a7e..c3a92ed9a61aeb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -7,6 +7,9 @@ import { ServerInjectOptions } from 'hapi'; import { omit } from 'lodash/fp'; +import * as utils from './utils'; +import * as updateRules from '../../rules/update_rules'; + import { updateRulesRoute } from './update_rules_route'; import { getFindResult, @@ -27,7 +30,12 @@ describe('update_rules', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); server = createMockServer(); config = createMockConfig(); @@ -66,6 +74,28 @@ describe('update_rules', () => { const { statusCode } = await inject(getUpdateRequest()); expect(statusCode).toBe(404); }); + + test('returns 500 when transform fails', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(utils, 'transform').mockReturnValue(null); + const { payload, statusCode } = await server.inject(getUpdateRequest()); + expect(JSON.parse(payload).message).toBe('Internal error transforming rules'); + expect(statusCode).toBe(500); + }); + + test('catches error if readRules throws error', async () => { + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + jest.spyOn(updateRules, 'updateRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getUpdateRequest()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('validation', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts index 593c55bcae9f21..b5b687c284b251 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts @@ -1251,9 +1251,10 @@ describe('utils', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); const parsedObjects = await createPromiseFromStreams([ - rulesObjectsStream, + ndJsonStream, + ...rulesObjectsStream, ]); const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, false); const isInstanceOfError = output[0] instanceof Error; @@ -1272,9 +1273,10 @@ describe('utils', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); const parsedObjects = await createPromiseFromStreams([ - rulesObjectsStream, + ndJsonStream, + ...rulesObjectsStream, ]); const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, false); @@ -1290,6 +1292,30 @@ describe('utils', () => { ]); }); + test('returns tuple of duplicate conflict error and single rule when rules with matching ids passed in and `overwrite` is false', async () => { + const rule = getSimpleRule('rule-1'); + delete rule.rule_id; + const rule2 = getSimpleRule('rule-1'); + delete rule2.rule_id; + const ndJsonStream = new Readable({ + read() { + this.push(`${JSON.stringify(rule)}\n`); + this.push(`${JSON.stringify(rule2)}\n`); + this.push(null); + }, + }); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); + const parsedObjects = await createPromiseFromStreams([ + ndJsonStream, + ...rulesObjectsStream, + ]); + const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, false); + const isInstanceOfError = output[0] instanceof Error; + + expect(isInstanceOfError).toEqual(true); + expect(errors).toEqual([]); + }); + test('returns tuple of empty duplicate errors array and single rule when rules with matching rule-ids passed in and `overwrite` is true', async () => { const rule = getSimpleRule('rule-1'); const rule2 = getSimpleRule('rule-1'); @@ -1300,9 +1326,10 @@ describe('utils', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); const parsedObjects = await createPromiseFromStreams([ - rulesObjectsStream, + ndJsonStream, + ...rulesObjectsStream, ]); const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, true); @@ -1320,9 +1347,10 @@ describe('utils', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); const parsedObjects = await createPromiseFromStreams([ - rulesObjectsStream, + ndJsonStream, + ...rulesObjectsStream, ]); const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, false); const isInstanceOfError = output[0] instanceof Error; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts index 198cdbfb9771d7..ab80e1570c31ce 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts @@ -180,9 +180,7 @@ export const transform = ( if (!ruleStatus && isAlertType(alert)) { return transformAlertToRule(alert); } - if (isAlertType(alert) && isRuleStatusFindType(ruleStatus)) { - return transformAlertToRule(alert, ruleStatus.saved_objects[0]); - } else if (isAlertType(alert) && isRuleStatusSavedObjectType(ruleStatus)) { + if (isAlertType(alert) && isRuleStatusSavedObjectType(ruleStatus)) { return transformAlertToRule(alert, ruleStatus); } else { return null; @@ -195,7 +193,7 @@ export const transformOrBulkError = ( ruleStatus?: unknown ): Partial | BulkError => { if (isAlertType(alert)) { - if (isRuleStatusFindType(ruleStatus)) { + if (isRuleStatusFindType(ruleStatus) && ruleStatus?.saved_objects.length > 0) { return transformAlertToRule(alert, ruleStatus?.saved_objects[0] ?? ruleStatus); } else { return transformAlertToRule(alert); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index 3e7ed4de6d8c6f..7086c62f817119 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -25,7 +25,12 @@ describe('set signal status', () => { let clients = clientsServiceMock.createClients(); beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); jest.spyOn(myUtils, 'getIndex').mockReturnValue('fakeindex'); server = createMockServer(); @@ -50,6 +55,15 @@ describe('set signal status', () => { const { statusCode } = await server.inject(getSetSignalStatusByQueryRequest()); expect(statusCode).toBe(200); }); + + test('catches error if callAsCurrentUser throws error', async () => { + clients.clusterClient.callAsCurrentUser.mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(getSetSignalStatusByQueryRequest()); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); describe('validation', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index dd3b8d3c99e0c8..ee3fd349a26eec 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -28,7 +28,7 @@ export const setSignalsStatusRouteDef = ( payload: setSignalsStatusSchema, }, }, - async handler(request: SignalsStatusRequest) { + async handler(request: SignalsStatusRequest, headers) { const { signal_ids: signalIds, query, status } = request.payload; const { clusterClient, spacesClient } = await getClients(request); const index = getIndex(spacesClient.getSpaceId, config); @@ -45,7 +45,7 @@ export const setSignalsStatusRouteDef = ( }; } try { - return clusterClient.callAsCurrentUser('updateByQuery', { + const updateByQueryResponse = await clusterClient.callAsCurrentUser('updateByQuery', { index, body: { script: { @@ -56,9 +56,15 @@ export const setSignalsStatusRouteDef = ( }, ignoreUnavailable: true, }); - } catch (exc) { - // error while getting or updating signal with id: id in signal index .siem-signals - return transformError(exc); + return updateByQueryResponse; + } catch (err) { + const error = transformError(err); + return headers + .response({ + message: error.message, + status_code: error.statusCode, + }) + .code(error.statusCode); } }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts index 9439adfcec3cb3..210ac9f3d7b01a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts @@ -127,5 +127,18 @@ describe('query for signal', () => { const { statusCode } = await server.inject(request); expect(statusCode).toBe(400); }); + test('catches error if deleteRules throws error', async () => { + const request: ServerInjectOptions = { + method: 'POST', + url: DETECTION_ENGINE_QUERY_SIGNALS_URL, + payload: { ...typicalSignalsQueryAggs(), ...typicalSignalsQuery() }, + }; + clients.clusterClient.callAsCurrentUser.mockImplementation(async () => { + throw new Error('Test error'); + }); + const { payload, statusCode } = await server.inject(request); + expect(JSON.parse(payload).message).toBe('Test error'); + expect(statusCode).toBe(500); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts index adb6e5f32921a0..7636329ecc306c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -28,21 +28,27 @@ export const querySignalsRouteDef = ( payload: querySignalsSchema, }, }, - async handler(request: SignalsQueryRequest) { + async handler(request: SignalsQueryRequest, headers) { const { query, aggs, _source, track_total_hits, size } = request.payload; const { clusterClient, spacesClient } = await getClients(request); const index = getIndex(spacesClient.getSpaceId, config); try { - return clusterClient.callAsCurrentUser('search', { + const searchSignalsIndexResult = await clusterClient.callAsCurrentUser('search', { index, body: { query, aggs, _source, track_total_hits, size }, ignoreUnavailable: true, }); - } catch (exc) { - // error while getting or updating signal with id: id in signal index .siem-signals - return transformError(exc); + return searchSignalsIndexResult; + } catch (err) { + const error = transformError(err); + return headers + .response({ + message: error.message, + status_code: error.statusCode, + }) + .code(error.statusCode); } }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts index 957ddd4ee6caac..3148083b4db267 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts @@ -15,6 +15,7 @@ import { ImportSuccessError, createImportErrorObject, transformImportError, + convertToSnakeCase, } from './utils'; import { createMockConfig } from './__mocks__'; @@ -312,4 +313,15 @@ describe('utils', () => { expect(index).toEqual('mockSignalsIndex-myspace'); }); }); + + describe('convertToSnakeCase', () => { + it('converts camelCase to snakeCase', () => { + const values = { myTestCamelCaseKey: 'something' }; + expect(convertToSnakeCase(values)).toEqual({ my_test_camel_case_key: 'something' }); + }); + it('returns empty object when object is empty', () => { + const values = {}; + expect(convertToSnakeCase(values)).toEqual({}); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts index 55832ab67dc6ba..36e1a814d8ec26 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts @@ -5,6 +5,7 @@ */ import Boom from 'boom'; +import { snakeCase } from 'lodash/fp'; import { APP_ID, SIGNALS_INDEX_KEY } from '../../../../common/constants'; import { LegacyServices } from '../../../types'; @@ -211,3 +212,11 @@ export const getIndex = (getSpaceId: () => string, config: LegacyServices['confi return `${signalsIndex}-${spaceId}`; }; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const convertToSnakeCase = >(obj: T): Partial | null => { + return Object.keys(obj).reduce((acc, item) => { + const newKey = snakeCase(item); + return { ...acc, [newKey]: obj[item] }; + }, {}); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts index d4b7c252e3e380..b1dc62f6fc90f0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts @@ -5,12 +5,10 @@ */ import { Readable } from 'stream'; import { createRulesStreamFromNdJson } from './create_rules_stream_from_ndjson'; -import { createPromiseFromStreams, createConcatStream } from 'src/legacy/utils/streams'; +import { createPromiseFromStreams } from 'src/legacy/utils/streams'; import { ImportRuleAlertRest } from '../types'; -const readStreamToCompletion = (stream: Readable) => { - return createPromiseFromStreams([stream, createConcatStream([])]); -}; +type PromiseFromStreams = ImportRuleAlertRest | Error; export const getOutputSample = (): Partial => ({ rule_id: 'rule-1', @@ -43,8 +41,11 @@ describe('create_rules_stream_from_ndjson', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); - const result = await readStreamToCompletion(rulesObjectsStream); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); + const result = await createPromiseFromStreams([ + ndJsonStream, + ...rulesObjectsStream, + ]); expect(result).toEqual([ { rule_id: 'rule-1', @@ -95,6 +96,22 @@ describe('create_rules_stream_from_ndjson', () => { ]); }); + test('returns error when ndjson stream is larger than limit', async () => { + const sample1 = getOutputSample(); + const sample2 = getOutputSample(); + sample2.rule_id = 'rule-2'; + const ndJsonStream = new Readable({ + read() { + this.push(getSampleAsNdjson(sample1)); + this.push(getSampleAsNdjson(sample2)); + }, + }); + const rulesObjectsStream = createRulesStreamFromNdJson(1); + await expect( + createPromiseFromStreams([ndJsonStream, ...rulesObjectsStream]) + ).rejects.toThrowError("Can't import more than 1 rules"); + }); + test('skips empty lines', async () => { const sample1 = getOutputSample(); const sample2 = getOutputSample(); @@ -108,8 +125,11 @@ describe('create_rules_stream_from_ndjson', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); - const result = await readStreamToCompletion(rulesObjectsStream); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); + const result = await createPromiseFromStreams([ + ndJsonStream, + ...rulesObjectsStream, + ]); expect(result).toEqual([ { rule_id: 'rule-1', @@ -172,8 +192,11 @@ describe('create_rules_stream_from_ndjson', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); - const result = await readStreamToCompletion(rulesObjectsStream); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); + const result = await createPromiseFromStreams([ + ndJsonStream, + ...rulesObjectsStream, + ]); expect(result).toEqual([ { rule_id: 'rule-1', @@ -236,8 +259,11 @@ describe('create_rules_stream_from_ndjson', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); - const result = await readStreamToCompletion(rulesObjectsStream); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); + const result = await createPromiseFromStreams([ + ndJsonStream, + ...rulesObjectsStream, + ]); const resultOrError = result as Error[]; expect(resultOrError[0]).toEqual({ rule_id: 'rule-1', @@ -300,8 +326,11 @@ describe('create_rules_stream_from_ndjson', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); - const result = await readStreamToCompletion(rulesObjectsStream); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); + const result = await createPromiseFromStreams([ + ndJsonStream, + ...rulesObjectsStream, + ]); const resultOrError = result as TypeError[]; expect(resultOrError[0]).toEqual({ rule_id: 'rule-1', @@ -366,8 +395,11 @@ describe('create_rules_stream_from_ndjson', () => { this.push(null); }, }); - const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000); - const result = await readStreamToCompletion(rulesObjectsStream); + const rulesObjectsStream = createRulesStreamFromNdJson(1000); + const result = await createPromiseFromStreams([ + ndJsonStream, + ...rulesObjectsStream, + ]); const resultOrError = result as TypeError[]; expect(resultOrError[1] instanceof TypeError).toEqual(true); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts index 6d58171a3245dc..ae0dfa20852aa1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Readable, Transform } from 'stream'; +import { Transform } from 'stream'; import { has, isString } from 'lodash/fp'; import { ImportRuleAlertRest } from '../types'; import { @@ -74,15 +74,13 @@ export const createLimitStream = (limit: number): Transform => { * Inspiration and the pattern of code followed is from: * saved_objects/lib/create_saved_objects_stream_from_ndjson.ts */ -export const createRulesStreamFromNdJson = ( - ndJsonStream: Readable, - ruleLimit: number -): Transform => { - return ndJsonStream - .pipe(createSplitStream('\n')) - .pipe(parseNdjsonStrings()) - .pipe(filterExportedCounts()) - .pipe(validateRules()) - .pipe(createLimitStream(ruleLimit)) - .pipe(createConcatStream([])); +export const createRulesStreamFromNdJson = (ruleLimit: number) => { + return [ + createSplitStream('\n'), + parseNdjsonStrings(), + filterExportedCounts(), + validateRules(), + createLimitStream(ruleLimit), + createConcatStream([]), + ]; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 98f5df48525300..44d3013263c656 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -10,9 +10,15 @@ import { getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; +import * as readRules from './read_rules'; import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; describe('get_export_by_object_ids', () => { + beforeEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + }); describe('getExportByObjectIds', () => { test('it exports object ids into an expected string with new line characters', async () => { const alertsClient = alertsClientMock.create(); @@ -119,6 +125,23 @@ describe('get_export_by_object_ids', () => { expect(exports).toEqual(expected); }); + test('it returns error when readRules throws error', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + jest.spyOn(readRules, 'readRules').mockImplementation(async () => { + throw new Error('Test error'); + }); + const objects = [{ rule_id: 'rule-1' }]; + const exports = await getRulesFromObjects(alertsClient, objects); + const expected: RulesErrors = { + exportedCount: 0, + missingRules: [{ rule_id: objects[0].rule_id }], + rules: [], + }; + expect(exports).toEqual(expected); + }); + test('it does not transform the rule if the rule is an immutable rule and designates it as a missing rule', async () => { const alertsClient = alertsClientMock.create(); const result = getResult(); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts index 45507a69f50c2f..aa1cce6f152382 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts @@ -8,7 +8,23 @@ import { readRules } from './read_rules'; import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; import { getResult, getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; +class TestError extends Error { + constructor() { + // Pass remaining arguments (including vendor specific ones) to parent constructor + super(); + + this.name = 'CustomError'; + this.output = { statusCode: 404 }; + } + public output: { statusCode: number }; +} + describe('read_rules', () => { + beforeEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + }); describe('readRules', () => { test('should return the output from alertsClient if id is set but ruleId is undefined', async () => { const alertsClient = alertsClientMock.create(); @@ -21,6 +37,49 @@ describe('read_rules', () => { }); expect(rule).toEqual(getResult()); }); + test('should return null if saved object found by alerts client given id is not alert type', async () => { + const alertsClient = alertsClientMock.create(); + const { alertTypeId, ...rest } = getResult(); + // @ts-ignore + alertsClient.get.mockImplementation(() => rest); + + const rule = await readRules({ + alertsClient, + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + ruleId: undefined, + }); + expect(rule).toEqual(null); + }); + + test('should return error if alerts client throws 404 error on get', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockImplementation(() => { + throw new TestError(); + }); + + const rule = await readRules({ + alertsClient, + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + ruleId: undefined, + }); + expect(rule).toEqual(null); + }); + + test('should return error if alerts client throws error on get', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockImplementation(() => { + throw new Error('Test error'); + }); + try { + await readRules({ + alertsClient, + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + ruleId: undefined, + }); + } catch (exc) { + expect(exc.message).toEqual('Test error'); + } + }); test('should return the output from alertsClient if id is set but ruleId is null', async () => { const alertsClient = alertsClientMock.create(); @@ -47,6 +106,20 @@ describe('read_rules', () => { expect(rule).toEqual(getResult()); }); + test('should return null if the output from alertsClient with ruleId set is empty', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + // @ts-ignore + alertsClient.find.mockResolvedValue({ data: [] }); + + const rule = await readRules({ + alertsClient, + id: undefined, + ruleId: 'rule-1', + }); + expect(rule).toEqual(null); + }); + test('should return the output from alertsClient if id is null but ruleId is set', async () => { const alertsClient = alertsClientMock.create(); alertsClient.get.mockResolvedValue(getResult()); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts index cbe6dbda8449f7..94e4e6357a4a0b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts @@ -31,7 +31,7 @@ export const readRules = async ({ return null; } } catch (err) { - if (err.output.statusCode === 404) { + if (err?.output?.statusCode === 404) { return null; } else { // throw non-404 as they would be 500 or other internal errors diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts index fa22765c143e1b..3d95e9868a1d68 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -197,10 +197,6 @@ export const isAlertType = (obj: unknown): obj is RuleAlertType => { return get('alertTypeId', obj) === SIGNALS_ID; }; -export const isRuleStatusAttributes = (obj: unknown): obj is IRuleStatusAttributes => { - return get('lastSuccessMessage', obj) != null; -}; - export const isRuleStatusSavedObjectType = ( obj: unknown ): obj is SavedObject => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signal_index_exists.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signal_index_exists.sh index b4a494a102b542..ec3e0595c7b089 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signal_index_exists.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signal_index_exists.sh @@ -10,7 +10,7 @@ set -e ./check_env_variables.sh # Example: ./signal_index_exists.sh -curl -s -k --head \ +curl -s -k -f \ -H 'Content-Type: application/json' \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - ${KIBANA_URL}${SPACE_URL}/api/detection_engine/index + ${KIBANA_URL}${SPACE_URL}/api/detection_engine/index > /dev/null diff --git a/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts index 38b95cc5772f2e..af6f8314b362a1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts @@ -25,13 +25,12 @@ import { TimelineData, TimelineDetailsData, TimelineEdges, - EventsOverTimeData, } from '../../graphql/types'; import { baseCategoryFields } from '../../utils/beat_schema/8.0.0'; import { reduceFields } from '../../utils/build_query/reduce_fields'; import { mergeFieldsWithHit, inspectStringifyObject } from '../../utils/build_query'; import { eventFieldsMap } from '../ecs_fields'; -import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; +import { FrameworkAdapter, FrameworkRequest } from '../framework'; import { TermAggregation } from '../types'; import { buildDetailsQuery, buildTimelineQuery } from './query.dsl'; @@ -43,10 +42,7 @@ import { LastEventTimeRequestOptions, RequestDetailsOptions, TimelineRequestOptions, - EventsActionGroupData, } from './types'; -import { buildEventsOverTimeQuery } from './query.events_over_time.dsl'; -import { MatrixOverTimeHistogramData } from '../../../public/graphql/types'; export class ElasticsearchEventsAdapter implements EventsAdapter { constructor(private readonly framework: FrameworkAdapter) {} @@ -129,65 +125,8 @@ export class ElasticsearchEventsAdapter implements EventsAdapter { lastSeen: getOr(null, 'aggregations.last_seen_event.value_as_string', response), }; } - - public async getEventsOverTime( - request: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - const dsl = buildEventsOverTimeQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - const totalCount = getOr(0, 'hits.total.value', response); - const eventsOverTimeBucket = getOr([], 'aggregations.eventActionGroup.buckets', response); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - return { - inspect, - matrixHistogramData: getEventsOverTimeByActionName(eventsOverTimeBucket), - totalCount, - }; - } } -/** - * Not in use at the moment, - * reserved this parser for next feature of switchign between total events and grouped events - */ -export const getTotalEventsOverTime = ( - data: EventsActionGroupData[] -): MatrixOverTimeHistogramData[] => { - return data && data.length > 0 - ? data.map(({ key, doc_count }) => ({ - x: key, - y: doc_count, - g: 'total events', - })) - : []; -}; - -const getEventsOverTimeByActionName = ( - data: EventsActionGroupData[] -): MatrixOverTimeHistogramData[] => { - let result: MatrixOverTimeHistogramData[] = []; - data.forEach(({ key: group, events }) => { - const eventsData = getOr([], 'buckets', events).map( - ({ key, doc_count }: { key: number; doc_count: number }) => ({ - x: key, - y: doc_count, - g: group, - }) - ); - result = [...result, ...eventsData]; - }); - - return result; -}; - export const formatEventsData = ( fields: readonly string[], hit: EventHit, diff --git a/x-pack/legacy/plugins/siem/server/lib/events/index.ts b/x-pack/legacy/plugins/siem/server/lib/events/index.ts index 9e2457904f8c0d..9c1f87aa3d8bf9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/events/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/events/index.ts @@ -5,7 +5,7 @@ */ import { LastEventTimeData, TimelineData, TimelineDetailsData } from '../../graphql/types'; -import { FrameworkRequest, RequestBasicOptions } from '../framework'; +import { FrameworkRequest } from '../framework'; export * from './elasticsearch_adapter'; import { EventsAdapter, @@ -13,7 +13,6 @@ import { LastEventTimeRequestOptions, RequestDetailsOptions, } from './types'; -import { EventsOverTimeData } from '../../../public/graphql/types'; export class Events { constructor(private readonly adapter: EventsAdapter) {} @@ -38,11 +37,4 @@ export class Events { ): Promise { return this.adapter.getLastEventTimeData(req, options); } - - public async getEventsOverTime( - req: FrameworkRequest, - options: RequestBasicOptions - ): Promise { - return this.adapter.getEventsOverTime(req, options); - } } diff --git a/x-pack/legacy/plugins/siem/server/lib/events/types.ts b/x-pack/legacy/plugins/siem/server/lib/events/types.ts index 2da0ff13638e1d..3a4a8705f73873 100644 --- a/x-pack/legacy/plugins/siem/server/lib/events/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/events/types.ts @@ -11,14 +11,8 @@ import { SourceConfiguration, TimelineData, TimelineDetailsData, - EventsOverTimeData, } from '../../graphql/types'; -import { - FrameworkRequest, - RequestOptions, - RequestOptionsPaginated, - RequestBasicOptions, -} from '../framework'; +import { FrameworkRequest, RequestOptions, RequestOptionsPaginated } from '../framework'; import { SearchHit } from '../types'; export interface EventsAdapter { @@ -31,10 +25,6 @@ export interface EventsAdapter { req: FrameworkRequest, options: LastEventTimeRequestOptions ): Promise; - getEventsOverTime( - req: FrameworkRequest, - options: RequestBasicOptions - ): Promise; } export interface TimelineRequestOptions extends RequestOptions { diff --git a/x-pack/legacy/plugins/siem/server/lib/framework/types.ts b/x-pack/legacy/plugins/siem/server/lib/framework/types.ts index 9fc78e6fb84fe0..7d049d1dcd1954 100644 --- a/x-pack/legacy/plugins/siem/server/lib/framework/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/framework/types.ts @@ -17,6 +17,7 @@ import { SourceConfiguration, TimerangeInput, Maybe, + HistogramType, } from '../../graphql/types'; export * from '../../utils/typed_resolvers'; @@ -117,7 +118,8 @@ export interface RequestBasicOptions { } export interface MatrixHistogramRequestOptions extends RequestBasicOptions { - stackByField?: Maybe; + stackByField: Maybe; + histogramType: HistogramType; } export interface RequestOptions extends RequestBasicOptions { diff --git a/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/elasticsearch_adapter.ts new file mode 100644 index 00000000000000..f661fe165130ee --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/elasticsearch_adapter.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getOr } from 'lodash/fp'; + +import { MatrixHistogramOverTimeData, HistogramType } from '../../graphql/types'; +import { inspectStringifyObject } from '../../utils/build_query'; +import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; +import { MatrixHistogramAdapter, MatrixHistogramDataConfig, MatrixHistogramHit } from './types'; +import { TermAggregation } from '../types'; +import { buildAnomaliesOverTimeQuery } from './query.anomalies_over_time.dsl'; +import { buildDnsHistogramQuery } from './query_dns_histogram.dsl'; +import { buildEventsOverTimeQuery } from './query.events_over_time.dsl'; +import { getDnsParsedData, getGenericData } from './utils'; +import { buildAuthenticationsOverTimeQuery } from './query.authentications_over_time.dsl'; +import { buildAlertsHistogramQuery } from './query_alerts.dsl'; + +const matrixHistogramConfig: MatrixHistogramDataConfig = { + [HistogramType.alerts]: { + buildDsl: buildAlertsHistogramQuery, + aggName: 'aggregations.alertsGroup.buckets', + parseKey: 'alerts.buckets', + }, + [HistogramType.anomalies]: { + buildDsl: buildAnomaliesOverTimeQuery, + aggName: 'aggregations.anomalyActionGroup.buckets', + parseKey: 'anomalies.buckets', + }, + [HistogramType.authentications]: { + buildDsl: buildAuthenticationsOverTimeQuery, + aggName: 'aggregations.eventActionGroup.buckets', + parseKey: 'events.buckets', + }, + [HistogramType.dns]: { + buildDsl: buildDnsHistogramQuery, + aggName: 'aggregations.NetworkDns.buckets', + parseKey: 'dns.buckets', + parser: getDnsParsedData, + }, + [HistogramType.events]: { + buildDsl: buildEventsOverTimeQuery, + aggName: 'aggregations.eventActionGroup.buckets', + parseKey: 'events.buckets', + }, +}; + +export class ElasticsearchMatrixHistogramAdapter implements MatrixHistogramAdapter { + constructor(private readonly framework: FrameworkAdapter) {} + + public async getHistogramData( + request: FrameworkRequest, + options: MatrixHistogramRequestOptions + ): Promise { + const myConfig = getOr(null, options.histogramType, matrixHistogramConfig); + if (myConfig == null) { + throw new Error(`This histogram type ${options.histogramType} is unknown to the server side`); + } + const dsl = myConfig.buildDsl(options); + const response = await this.framework.callWithRequest< + MatrixHistogramHit, + TermAggregation + >(request, 'search', dsl); + const totalCount = getOr(0, 'hits.total.value', response); + const matrixHistogramData = getOr([], myConfig.aggName, response); + const inspect = { + dsl: [inspectStringifyObject(dsl)], + response: [inspectStringifyObject(response)], + }; + + return { + inspect, + matrixHistogramData: myConfig.parser + ? myConfig.parser(matrixHistogramData, myConfig.parseKey) + : getGenericData(matrixHistogramData, myConfig.parseKey), + totalCount, + }; + } +} diff --git a/x-pack/legacy/plugins/siem/server/lib/alerts/elasticseatch_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/elasticseatch_adapter.test.ts similarity index 86% rename from x-pack/legacy/plugins/siem/server/lib/alerts/elasticseatch_adapter.test.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/elasticseatch_adapter.test.ts index 210c97892e25c8..0b63785d2203bc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/alerts/elasticseatch_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/elasticseatch_adapter.test.ts @@ -6,7 +6,7 @@ import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; import expect from '@kbn/expect'; -import { ElasticsearchAlertsAdapter } from './elasticsearch_adapter'; +import { ElasticsearchMatrixHistogramAdapter } from './elasticsearch_adapter'; import { mockRequest, mockOptions, @@ -15,7 +15,7 @@ import { mockAlertsHistogramDataFormattedResponse, } from './mock'; -jest.mock('./query.dsl', () => { +jest.mock('./query_alerts.dsl', () => { return { buildAlertsHistogramQuery: jest.fn(() => mockAlertsHistogramQueryDsl), }; @@ -37,8 +37,8 @@ describe('alerts elasticsearch_adapter', () => { callWithRequest: mockCallWithRequest, })); - const EsNetworkTimelineAlerts = new ElasticsearchAlertsAdapter(mockFramework); - const data = await EsNetworkTimelineAlerts.getAlertsHistogramData( + const adapter = new ElasticsearchMatrixHistogramAdapter(mockFramework); + const data = await adapter.getHistogramData( (mockRequest as unknown) as FrameworkRequest, (mockOptions as unknown) as MatrixHistogramRequestOptions ); diff --git a/x-pack/legacy/plugins/siem/server/lib/alerts/index.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/index.ts similarity index 55% rename from x-pack/legacy/plugins/siem/server/lib/alerts/index.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/index.ts index 9cfb1841edfefe..900a6ab619ae03 100644 --- a/x-pack/legacy/plugins/siem/server/lib/alerts/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/index.ts @@ -6,16 +6,16 @@ import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; export * from './elasticsearch_adapter'; -import { AlertsAdapter } from './types'; -import { AlertsOverTimeData } from '../../graphql/types'; +import { MatrixHistogramAdapter } from './types'; +import { MatrixHistogramOverTimeData } from '../../graphql/types'; -export class Alerts { - constructor(private readonly adapter: AlertsAdapter) {} +export class MatrixHistogram { + constructor(private readonly adapter: MatrixHistogramAdapter) {} - public async getAlertsHistogramData( + public async getMatrixHistogramData( req: FrameworkRequest, options: MatrixHistogramRequestOptions - ): Promise { - return this.adapter.getAlertsHistogramData(req, options); + ): Promise { + return this.adapter.getHistogramData(req, options); } } diff --git a/x-pack/legacy/plugins/siem/server/lib/alerts/mock.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/mock.ts similarity index 95% rename from x-pack/legacy/plugins/siem/server/lib/alerts/mock.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/mock.ts index fe0b6673f3191a..3e51e926bea875 100644 --- a/x-pack/legacy/plugins/siem/server/lib/alerts/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/mock.ts @@ -5,6 +5,7 @@ */ import { defaultIndexPattern } from '../../../default_index_pattern'; +import { HistogramType } from '../../graphql/types'; export const mockAlertsHistogramDataResponse = { took: 513, @@ -36,7 +37,7 @@ export const mockAlertsHistogramDataResponse = { hits: [], }, aggregations: { - alertsByModuleGroup: { + alertsGroup: { doc_count_error_upper_bound: 0, sum_other_doc_count: 802087, buckets: [ @@ -112,4 +113,6 @@ export const mockOptions = { }, defaultIndex: defaultIndexPattern, filterQuery: '', + stackByField: 'event.module', + histogramType: HistogramType.alerts, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/anomalies/query.anomalies_over_time.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query.anomalies_over_time.dsl.ts similarity index 100% rename from x-pack/legacy/plugins/siem/server/lib/anomalies/query.anomalies_over_time.dsl.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query.anomalies_over_time.dsl.ts diff --git a/x-pack/legacy/plugins/siem/server/lib/authentications/query.authentications_over_time.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query.authentications_over_time.dsl.ts similarity index 100% rename from x-pack/legacy/plugins/siem/server/lib/authentications/query.authentications_over_time.dsl.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query.authentications_over_time.dsl.ts diff --git a/x-pack/legacy/plugins/siem/server/lib/events/query.events_over_time.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query.events_over_time.dsl.ts similarity index 100% rename from x-pack/legacy/plugins/siem/server/lib/events/query.events_over_time.dsl.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query.events_over_time.dsl.ts diff --git a/x-pack/legacy/plugins/siem/server/lib/alerts/query.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query_alerts.dsl.ts similarity index 98% rename from x-pack/legacy/plugins/siem/server/lib/alerts/query.dsl.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query_alerts.dsl.ts index eb823271975439..4963f01d67a4f0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/alerts/query.dsl.ts +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query_alerts.dsl.ts @@ -82,7 +82,7 @@ export const buildAlertsHistogramQuery = ({ }, }; return { - alertsByModuleGroup: { + alertsGroup: { terms: { field: stackByField, missing: 'All others', diff --git a/x-pack/legacy/plugins/siem/server/lib/network/query_dns_histogram.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query_dns_histogram.dsl.ts similarity index 98% rename from x-pack/legacy/plugins/siem/server/lib/network/query_dns_histogram.dsl.ts rename to x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query_dns_histogram.dsl.ts index 1ce324e0ffff89..a6c75fe01eb151 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/query_dns_histogram.dsl.ts +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/query_dns_histogram.dsl.ts @@ -42,7 +42,7 @@ export const buildDnsHistogramQuery = ({ NetworkDns: { ...dateHistogram, aggs: { - histogram: { + dns: { terms: { field: stackByField, order: { diff --git a/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/types.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/types.ts new file mode 100644 index 00000000000000..87ea4b81f5fba9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/types.ts @@ -0,0 +1,144 @@ +/* + * 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 { + MatrixHistogramOverTimeData, + HistogramType, + MatrixOverTimeHistogramData, +} from '../../graphql/types'; +import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework'; +import { SearchHit } from '../types'; +import { EventHit } from '../events/types'; +import { AuthenticationHit } from '../authentications/types'; + +export interface HistogramBucket { + key: number; + doc_count: number; +} + +interface AlertsGroupData { + key: string; + doc_count: number; + alerts: { + buckets: HistogramBucket[]; + }; +} + +interface AnomaliesOverTimeHistogramData { + key_as_string: string; + key: number; + doc_count: number; +} + +export interface AnomaliesActionGroupData { + key: number; + anomalies: { + bucket: AnomaliesOverTimeHistogramData[]; + }; + doc_count: number; +} + +export interface AnomalySource { + [field: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any +} + +export interface AnomalyHit extends SearchHit { + sort: string[]; + _source: AnomalySource; + aggregations: { + [agg: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any + }; +} + +interface EventsOverTimeHistogramData { + key_as_string: string; + key: number; + doc_count: number; +} + +export interface EventsActionGroupData { + key: number; + events: { + bucket: EventsOverTimeHistogramData[]; + }; + doc_count: number; +} + +export interface DnsHistogramSubBucket { + key: string; + doc_count: number; + orderAgg: { + value: number; + }; +} +interface DnsHistogramBucket { + doc_count_error_upper_bound: number; + sum_other_doc_count: number; + buckets: DnsHistogramSubBucket[]; +} + +export interface DnsHistogramGroupData { + key: number; + doc_count: number; + key_as_string: string; + histogram: DnsHistogramBucket; +} + +export interface MatrixHistogramSchema { + buildDsl: (options: MatrixHistogramRequestOptions) => {}; + aggName: string; + parseKey: string; + parser?: ( + data: MatrixHistogramParseData, + keyBucket: string + ) => MatrixOverTimeHistogramData[]; +} + +export type MatrixHistogramParseData = T extends HistogramType.alerts + ? AlertsGroupData[] + : T extends HistogramType.anomalies + ? AnomaliesActionGroupData[] + : T extends HistogramType.dns + ? DnsHistogramGroupData[] + : T extends HistogramType.authentications + ? AuthenticationsActionGroupData[] + : T extends HistogramType.events + ? EventsActionGroupData[] + : never; + +export type MatrixHistogramHit = T extends HistogramType.alerts + ? EventHit + : T extends HistogramType.anomalies + ? AnomalyHit + : T extends HistogramType.dns + ? EventHit + : T extends HistogramType.authentications + ? AuthenticationHit + : T extends HistogramType.events + ? EventHit + : never; + +export type MatrixHistogramDataConfig = Record>; +interface AuthenticationsOverTimeHistogramData { + key_as_string: string; + key: number; + doc_count: number; +} + +export interface AuthenticationsActionGroupData { + key: number; + events: { + bucket: AuthenticationsOverTimeHistogramData[]; + }; + doc_count: number; +} + +export interface MatrixHistogramAdapter { + getHistogramData( + request: FrameworkRequest, + options: MatrixHistogramRequestOptions + ): Promise; +} diff --git a/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/utils.ts b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/utils.ts new file mode 100644 index 00000000000000..67568b96fee906 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/matrix_histogram/utils.ts @@ -0,0 +1,48 @@ +/* + * 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 { get, getOr } from 'lodash/fp'; +import { MatrixHistogramParseData, DnsHistogramSubBucket, HistogramBucket } from './types'; +import { MatrixOverTimeHistogramData } from '../../graphql/types'; + +export const getDnsParsedData = ( + data: MatrixHistogramParseData, + keyBucket: string +): MatrixOverTimeHistogramData[] => { + let result: MatrixOverTimeHistogramData[] = []; + data.forEach((bucketData: unknown) => { + const time = get('key', bucketData); + const histData = getOr([], keyBucket, bucketData).map( + ({ key, doc_count }: DnsHistogramSubBucket) => ({ + x: time, + y: doc_count, + g: key, + }) + ); + result = [...result, ...histData]; + }); + return result; +}; + +export const getGenericData = ( + data: MatrixHistogramParseData, + keyBucket: string +): MatrixOverTimeHistogramData[] => { + let result: MatrixOverTimeHistogramData[] = []; + data.forEach((bucketData: unknown) => { + const group = get('key', bucketData); + const histData = getOr([], keyBucket, bucketData).map( + ({ key, doc_count }: HistogramBucket) => ({ + x: key, + y: doc_count, + g: group, + }) + ); + result = [...result, ...histData]; + }); + + return result; +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts index 4bd980fd2ff803..39babc58ee138c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts @@ -18,16 +18,9 @@ import { NetworkHttpData, NetworkHttpEdges, NetworkTopNFlowEdges, - NetworkDsOverTimeData, - MatrixOverTimeHistogramData, } from '../../graphql/types'; import { inspectStringifyObject } from '../../utils/build_query'; -import { - DatabaseSearchResponse, - FrameworkAdapter, - FrameworkRequest, - MatrixHistogramRequestOptions, -} from '../framework'; +import { DatabaseSearchResponse, FrameworkAdapter, FrameworkRequest } from '../framework'; import { TermAggregation } from '../types'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants'; @@ -38,7 +31,6 @@ import { NetworkTopNFlowRequestOptions, } from './index'; import { buildDnsQuery } from './query_dns.dsl'; -import { buildDnsHistogramQuery } from './query_dns_histogram.dsl'; import { buildTopNFlowQuery, getOppositeField } from './query_top_n_flow.dsl'; import { buildHttpQuery } from './query_http.dsl'; import { buildTopCountriesQuery } from './query_top_countries.dsl'; @@ -48,9 +40,7 @@ import { NetworkTopCountriesBuckets, NetworkHttpBuckets, NetworkTopNFlowBuckets, - DnsHistogramGroupData, } from './types'; -import { EventHit } from '../events/types'; export class ElasticsearchNetworkAdapter implements NetworkAdapter { constructor(private readonly framework: FrameworkAdapter) {} @@ -202,41 +192,8 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter { totalCount, }; } - - public async getNetworkDnsHistogramData( - request: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - const dsl = buildDnsHistogramQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - const totalCount = getOr(0, 'hits.total.value', response); - const matrixHistogramData = getOr([], 'aggregations.NetworkDns.buckets', response); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - return { - inspect, - matrixHistogramData: getHistogramData(matrixHistogramData), - totalCount, - }; - } } -const getHistogramData = (data: DnsHistogramGroupData[]): MatrixOverTimeHistogramData[] => { - return data.reduce( - (acc: MatrixOverTimeHistogramData[], { key: time, histogram: { buckets } }) => { - const temp = buckets.map(({ key, doc_count }) => ({ x: time, y: doc_count, g: key })); - return [...acc, ...temp]; - }, - [] - ); -}; - const getTopNFlowEdges = ( response: DatabaseSearchResponse, options: NetworkTopNFlowRequestOptions diff --git a/x-pack/legacy/plugins/siem/server/lib/network/index.ts b/x-pack/legacy/plugins/siem/server/lib/network/index.ts index cbcd33b753d8ad..42ce9f0726ddb6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/index.ts @@ -14,13 +14,8 @@ import { NetworkTopCountriesData, NetworkTopNFlowData, NetworkTopTablesSortField, - NetworkDsOverTimeData, } from '../../graphql/types'; -import { - FrameworkRequest, - RequestOptionsPaginated, - MatrixHistogramRequestOptions, -} from '../framework'; +import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; export * from './elasticsearch_adapter'; import { NetworkAdapter } from './types'; @@ -73,13 +68,6 @@ export class Network { return this.adapter.getNetworkDns(req, options); } - public async getNetworkDnsHistogramData( - req: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise { - return this.adapter.getNetworkDnsHistogramData(req, options); - } - public async getNetworkHttp( req: FrameworkRequest, options: NetworkHttpRequestOptions diff --git a/x-pack/legacy/plugins/siem/server/lib/network/types.ts b/x-pack/legacy/plugins/siem/server/lib/network/types.ts index b5563f9a2fef12..b7848be0971514 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/types.ts @@ -9,13 +9,8 @@ import { NetworkHttpData, NetworkTopCountriesData, NetworkTopNFlowData, - NetworkDsOverTimeData, } from '../../graphql/types'; -import { - FrameworkRequest, - RequestOptionsPaginated, - MatrixHistogramRequestOptions, -} from '../framework'; +import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; import { TotalValue } from '../types'; import { NetworkDnsRequestOptions } from '.'; @@ -29,10 +24,6 @@ export interface NetworkAdapter { options: RequestOptionsPaginated ): Promise; getNetworkDns(req: FrameworkRequest, options: NetworkDnsRequestOptions): Promise; - getNetworkDnsHistogramData( - request: FrameworkRequest, - options: MatrixHistogramRequestOptions - ): Promise; getNetworkHttp(req: FrameworkRequest, options: RequestOptionsPaginated): Promise; } diff --git a/x-pack/legacy/plugins/siem/server/lib/types.ts b/x-pack/legacy/plugins/siem/server/lib/types.ts index 34a50cf9624129..323ced734d24ba 100644 --- a/x-pack/legacy/plugins/siem/server/lib/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/types.ts @@ -8,7 +8,6 @@ import { AuthenticatedUser } from '../../../../../plugins/security/public'; import { RequestHandlerContext } from '../../../../../../src/core/server'; export { ConfigType as Configuration } from '../../../../../plugins/siem/server'; -import { Anomalies } from './anomalies'; import { Authentications } from './authentications'; import { Events } from './events'; import { FrameworkAdapter, FrameworkRequest } from './framework'; @@ -26,18 +25,17 @@ import { Note } from './note/saved_object'; import { PinnedEvent } from './pinned_event/saved_object'; import { Timeline } from './timeline/saved_object'; import { TLS } from './tls'; -import { Alerts } from './alerts'; +import { MatrixHistogram } from './matrix_histogram'; export * from './hosts'; export interface AppDomainLibs { - alerts: Alerts; - anomalies: Anomalies; authentications: Authentications; events: Events; fields: IndexFields; hosts: Hosts; ipDetails: IpDetails; + matrixHistogram: MatrixHistogram; network: Network; kpiNetwork: KpiNetwork; overview: Overview; diff --git a/x-pack/legacy/plugins/uptime/common/graphql/introspection.json b/x-pack/legacy/plugins/uptime/common/graphql/introspection.json index e5d9816ebd28e8..18f26552d31535 100644 --- a/x-pack/legacy/plugins/uptime/common/graphql/introspection.json +++ b/x-pack/legacy/plugins/uptime/common/graphql/introspection.json @@ -72,18 +72,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "getDocCount", - "description": "Gets the number of documents in the target index", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "DocCount", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "getMonitors", "description": "", diff --git a/x-pack/legacy/plugins/uptime/common/graphql/types.ts b/x-pack/legacy/plugins/uptime/common/graphql/types.ts index c58dd9111cc3f7..643c419be04114 100644 --- a/x-pack/legacy/plugins/uptime/common/graphql/types.ts +++ b/x-pack/legacy/plugins/uptime/common/graphql/types.ts @@ -17,8 +17,6 @@ export type UnsignedInteger = any; export interface Query { /** Get a list of all recorded pings for all monitors */ allPings: PingResults; - /** Gets the number of documents in the target index */ - getDocCount: DocCount; getMonitors?: LatestMonitorsResult | null; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts index 15f276174e2cf1..7eb18404decfde 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts @@ -22,13 +22,9 @@ const getApiPath = (page?: UptimePage) => { }; const logPageLoad = async (fetch: HttpHandler, page?: UptimePage) => { - try { - await fetch(getApiPath(page), { - method: 'POST', - }); - } catch (e) { - throw e; - } + await fetch(getApiPath(page), { + method: 'POST', + }); }; export const useUptimeTelemetry = (page?: UptimePage) => { diff --git a/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts b/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts index 45b073086d2129..e2b076d5708432 100644 --- a/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts +++ b/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts @@ -47,10 +47,10 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( ? JSON.parse(decodeURIComponent(pagination)) : CONTEXT_DEFAULTS.CURSOR_PAGINATION; const [ - totalSummaryCount, + indexStatus, { summaries, nextPagePagination, prevPagePagination }, ] = await Promise.all([ - libs.requests.getDocCount({ callES: APICaller }), + libs.requests.getIndexStatus({ callES: APICaller }), libs.requests.getMonitorStates({ callES: APICaller, dateRangeStart, @@ -63,6 +63,9 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( statusFilter: statusFilter || undefined, }), ]); + + const totalSummaryCount = indexStatus?.docCount ?? { count: undefined }; + return { summaries, nextPagePagination, @@ -71,7 +74,7 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( }; }, async getStatesIndexStatus(_resolver, {}, { APICaller }): Promise { - return await libs.requests.getStatesIndexStatus({ callES: APICaller }); + return await libs.requests.getIndexStatus({ callES: APICaller }); }, }, }; diff --git a/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts b/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts index dea7469ab62178..de83a9ced16b2d 100644 --- a/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts +++ b/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts @@ -5,7 +5,7 @@ */ import { UMResolver } from '../../../common/graphql/resolver_types'; -import { AllPingsQueryArgs, DocCount, PingResults } from '../../../common/graphql/types'; +import { AllPingsQueryArgs, PingResults } from '../../../common/graphql/types'; import { UMServerLibs } from '../../lib/lib'; import { UMContext } from '../types'; import { CreateUMGraphQLResolvers } from '../types'; @@ -17,11 +17,8 @@ export type UMAllPingsResolver = UMResolver< UMContext >; -export type UMGetDocCountResolver = UMResolver, any, never, UMContext>; - export interface UMPingResolver { allPings: () => PingResults; - getDocCount: () => number; } export const createPingsResolvers: CreateUMGraphQLResolvers = ( @@ -29,7 +26,6 @@ export const createPingsResolvers: CreateUMGraphQLResolvers = ( ): { Query: { allPings: UMAllPingsResolver; - getDocCount: UMGetDocCountResolver; }; } => ({ Query: { @@ -49,8 +45,5 @@ export const createPingsResolvers: CreateUMGraphQLResolvers = ( location, }); }, - async getDocCount(_resolver, _args, { APICaller }): Promise { - return libs.requests.getDocCount({ callES: APICaller }); - }, }, }); diff --git a/x-pack/legacy/plugins/uptime/server/graphql/pings/schema.gql.ts b/x-pack/legacy/plugins/uptime/server/graphql/pings/schema.gql.ts index 37c5400ff9d06e..4b7ccbec374644 100644 --- a/x-pack/legacy/plugins/uptime/server/graphql/pings/schema.gql.ts +++ b/x-pack/legacy/plugins/uptime/server/graphql/pings/schema.gql.ts @@ -38,9 +38,6 @@ export const pingsSchema = gql` "Optional: agent location to filter by." location: String ): PingResults! - - "Gets the number of documents in the target index" - getDocCount: DocCount! } type ContainerImage { diff --git a/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts b/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts index 0dcbc0a424b5eb..1607c36f1d1b78 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts @@ -7,6 +7,5 @@ export { getFilterClause } from './get_filter_clause'; export { parseRelativeDate } from './get_histogram_interval'; export { getHistogramIntervalFormatted } from './get_histogram_interval_formatted'; -export { parseFilterQuery } from './parse_filter_query'; export { assertCloseTo } from './assert_close_to'; export { objectValuesToArrays } from './object_to_array'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/__tests__/get_doc_count.test.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/__tests__/get_doc_count.test.ts deleted file mode 100644 index 7dfb0314fe8ddb..00000000000000 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/__tests__/get_doc_count.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getDocCount } from '../get_doc_count'; - -describe('getDocCount', () => { - let mockHits: any[]; - let mockEsCountResult: any; - - beforeEach(() => { - mockHits = [ - { - _source: { - '@timestamp': '2018-10-30T18:51:59.792Z', - }, - }, - { - _source: { - '@timestamp': '2018-10-30T18:53:59.792Z', - }, - }, - { - _source: { - '@timestamp': '2018-10-30T18:55:59.792Z', - }, - }, - ]; - mockEsCountResult = { - count: mockHits.length, - }; - }); - - it('returns data in appropriate shape', async () => { - const mockEsClient = jest.fn(); - mockEsClient.mockReturnValue(mockEsCountResult); - const { count } = await getDocCount({ callES: mockEsClient }); - expect(count).toEqual(3); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_doc_count.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_doc_count.ts deleted file mode 100644 index 68c122aaaa9fb4..00000000000000 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_doc_count.ts +++ /dev/null @@ -1,15 +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 { DocCount } from '../../../common/graphql/types'; -import { INDEX_NAMES } from '../../../common/constants'; -import { UMElasticsearchQueryFn } from '../adapters'; - -export const getDocCount: UMElasticsearchQueryFn<{}, DocCount> = async ({ callES }) => { - const { count } = await callES('count', { index: INDEX_NAMES.HEARTBEAT }); - - return { count }; -}; diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_states_index_status.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_index_status.ts similarity index 84% rename from x-pack/legacy/plugins/uptime/server/lib/requests/get_states_index_status.ts rename to x-pack/legacy/plugins/uptime/server/lib/requests/get_index_status.ts index 5044b9a6932cf0..e801b05d057f47 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_states_index_status.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/get_index_status.ts @@ -8,9 +8,7 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { StatesIndexStatus } from '../../../common/graphql/types'; import { INDEX_NAMES } from '../../../common/constants'; -export const getStatesIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = async ({ - callES, -}) => { +export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = async ({ callES }) => { const { _shards: { total }, count, diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts index 3b448dc31659b7..7b8ca4708255c8 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts @@ -5,7 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { parseFilterQuery, getFilterClause } from '../helper'; +import { getFilterClause } from '../helper'; import { INDEX_NAMES, QUERY } from '../../../common/constants'; import { HistogramQueryResult } from './types'; import { HistogramResult } from '../../../common/types'; @@ -27,7 +27,7 @@ export const getPingHistogram: UMElasticsearchQueryFn< GetPingHistogramParams, HistogramResult > = async ({ callES, from, to, filters, monitorId, statusFilter }) => { - const boolFilters = parseFilterQuery(filters); + const boolFilters = filters ? JSON.parse(filters) : null; const additionalFilters = []; if (monitorId) { additionalFilters.push({ match: { 'monitor.id': monitorId } }); diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts index 62369711460159..8d84c0f4d6769f 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts @@ -6,7 +6,7 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { Snapshot } from '../../../common/runtime_types'; -import { QueryContext, MonitorGroupIterator } from './search'; +import { QueryContext } from './search'; import { CONTEXT_DEFAULTS, INDEX_NAMES } from '../../../common/constants'; export interface GetSnapshotCountParams { @@ -16,49 +16,6 @@ export interface GetSnapshotCountParams { statusFilter?: string; } -const fastStatusCount = async (context: QueryContext): Promise => { - const params = { - index: INDEX_NAMES.HEARTBEAT, - body: { - size: 0, - query: { bool: { filter: await context.dateAndCustomFilters() } }, - aggs: { - unique: { - // We set the precision threshold to 40k which is the max precision supported by cardinality - cardinality: { field: 'monitor.id', precision_threshold: 40000 }, - }, - down: { - filter: { range: { 'summary.down': { gt: 0 } } }, - aggs: { - unique: { cardinality: { field: 'monitor.id', precision_threshold: 40000 } }, - }, - }, - }, - }, - }; - - const statistics = await context.search(params); - const total = statistics.aggregations.unique.value; - const down = statistics.aggregations.down.unique.value; - - return { - total, - down, - up: total - down, - }; -}; - -const slowStatusCount = async (context: QueryContext, status: string): Promise => { - const downContext = context.clone(); - downContext.statusFilter = status; - const iterator = new MonitorGroupIterator(downContext); - let count = 0; - while (await iterator.next()) { - count++; - } - return count; -}; - export const getSnapshotCount: UMElasticsearchQueryFn = async ({ callES, dateRangeStart, @@ -81,18 +38,7 @@ export const getSnapshotCount: UMElasticsearchQueryFn = - counts.up > counts.down ? ['down', 'up'] : ['up', 'down']; - counts[leastCommonStatus] = await slowStatusCount(context, leastCommonStatus); - counts[mostCommonStatus] = counts.total - counts[leastCommonStatus]; - } + const counts = await statusCount(context); return { total: statusFilter ? counts[statusFilter] : counts.total, @@ -100,3 +46,139 @@ export const getSnapshotCount: UMElasticsearchQueryFn => { + const res = await context.search({ + index: INDEX_NAMES.HEARTBEAT, + body: statusCountBody(await context.dateAndCustomFilters()), + }); + + return res.aggregations.counts.value; +}; + +const statusCountBody = (filters: any): any => { + return { + size: 0, + query: { + bool: { + filter: [ + { + exists: { + field: 'summary', + }, + }, + filters, + ], + }, + }, + aggs: { + counts: { + scripted_metric: { + init_script: 'state.locStatus = new HashMap(); state.totalDocs = 0;', + map_script: ` + def loc = doc["observer.geo.name"].size() == 0 ? "" : doc["observer.geo.name"][0]; + + // One concern here is memory since we could build pretty gigantic maps. I've opted to + // stick to a simple map to reduce memory overhead. This means we do + // a little string parsing to treat these strings as records that stay lexicographically + // sortable (which is important later). + // We encode the ID and location as $id.len:$id$loc + String id = doc["monitor.id"][0]; + String idLenDelim = Integer.toHexString(id.length()) + ":" + id; + String idLoc = loc == null ? idLenDelim : idLenDelim + loc; + + String status = doc["summary.down"][0] > 0 ? "d" : "u"; + String timeAndStatus = doc["@timestamp"][0].toInstant().toEpochMilli().toString() + status; + state.locStatus[idLoc] = timeAndStatus; + state.totalDocs++; + `, + combine_script: ` + return state; + `, + reduce_script: ` + // Use a treemap since it's traversable in sorted order. + // This is important later. + TreeMap locStatus = new TreeMap(); + long totalDocs = 0; + int uniqueIds = 0; + for (state in states) { + totalDocs += state.totalDocs; + for (entry in state.locStatus.entrySet()) { + // Update the value for the given key if we have a more recent check from this location. + locStatus.merge(entry.getKey(), entry.getValue(), (a,b) -> a.compareTo(b) > 0 ? a : b) + } + } + + HashMap locTotals = new HashMap(); + int total = 0; + int down = 0; + String curId = ""; + boolean curIdDown = false; + // We now iterate through our tree map in order, which means records for a given ID + // always are encountered one after the other. This saves us having to make an intermediate + // map. + for (entry in locStatus.entrySet()) { + String idLoc = entry.getKey(); + String timeStatus = entry.getValue(); + + // Parse the length delimited id/location strings described in the map section + int colonIndex = idLoc.indexOf(":"); + int idEnd = Integer.parseInt(idLoc.substring(0, colonIndex), 16) + colonIndex + 1; + String id = idLoc.substring(colonIndex + 1, idEnd); + String loc = idLoc.substring(idEnd, idLoc.length()); + String status = timeStatus.substring(timeStatus.length() - 1); + + // Here we increment counters for the up/down key per location + // We also create a new hashmap in locTotals if we've never seen this location + // before. + locTotals.compute(loc, (k,v) -> { + HashMap res = v; + if (v == null) { + res = new HashMap(); + res.put('up', 0); + res.put('down', 0); + } + + if (status == 'u') { + res.up++; + } else { + res.down++; + } + + return res; + }); + + + // We've encountered a new ID + if (curId != id) { + total++; + curId = id; + if (status == "d") { + curIdDown = true; + down++; + } else { + curIdDown = false; + } + } else if (!curIdDown) { + if (status == "d") { + curIdDown = true; + down++; + } else { + curIdDown = false; + } + } + } + + Map result = new HashMap(); + result.total = total; + result.location_totals = locTotals; + result.up = total - down; + result.down = down; + result.totalDocs = totalDocs; + return result; + `, + }, + }, + }, + }; +}; diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/index.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/index.ts index f41b7257524fdd..97517b7faad356 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getDocCount } from './get_doc_count'; export { getFilterBar, GetFilterBarParams } from './get_filter_bar'; export { getUptimeIndexPattern as getIndexPattern } from './get_index_pattern'; export { getLatestMonitor, GetLatestMonitorParams } from './get_latest_monitor'; @@ -17,4 +16,4 @@ export { getPings, GetPingsParams } from './get_pings'; export { getPingHistogram, GetPingHistogramParams } from './get_ping_histogram'; export { UptimeRequests } from './uptime_requests'; export { getSnapshotCount, GetSnapshotCountParams } from './get_snapshot_counts'; -export { getStatesIndexStatus } from './get_states_index_status'; +export { getIndexStatus } from './get_index_status'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/types.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/types.ts index 7f65e80113d8fe..e17eb546712a94 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/types.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DocCount, Ping, PingResults } from '../../../common/graphql/types'; +import { Ping, PingResults } from '../../../common/graphql/types'; import { UMElasticsearchQueryFn } from '../adapters'; import { GetPingHistogramParams, HistogramResult } from '../../../common/types'; @@ -54,11 +54,6 @@ export interface UMPingsAdapter { getLatestMonitorStatus: UMElasticsearchQueryFn; getPingHistogram: UMElasticsearchQueryFn; - - /** - * Gets data used for a composite histogram for the currently-running monitors. - */ - getDocCount: UMElasticsearchQueryFn<{}, DocCount>; } export interface HistogramQueryResult { diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/uptime_requests.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/uptime_requests.ts index 182c944e8388ad..73be850306202e 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/requests/uptime_requests.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/requests/uptime_requests.ts @@ -5,13 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { - DocCount, - Ping, - MonitorChart, - PingResults, - StatesIndexStatus, -} from '../../../common/graphql/types'; +import { Ping, MonitorChart, PingResults, StatesIndexStatus } from '../../../common/graphql/types'; import { GetFilterBarParams, GetLatestMonitorParams, @@ -36,7 +30,6 @@ import { HistogramResult } from '../../../common/types'; type ESQ = UMElasticsearchQueryFn; export interface UptimeRequests { - getDocCount: ESQ<{}, DocCount>; getFilterBar: ESQ; getIndexPattern: ESQ; getLatestMonitor: ESQ; @@ -48,5 +41,5 @@ export interface UptimeRequests { getPings: ESQ; getPingHistogram: ESQ; getSnapshotCount: ESQ; - getStatesIndexStatus: ESQ<{}, StatesIndexStatus>; + getIndexStatus: ESQ<{}, StatesIndexStatus>; } diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts new file mode 100644 index 00000000000000..4c9dc78eb41e9d --- /dev/null +++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { agentConfigurationIntakeRt } from './index'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('agentConfigurationIntakeRt', () => { + it('is valid when required parameters are given', () => { + const config = { + service: {}, + settings: {} + }; + + expect(isConfigValid(config)).toBe(true); + }); + + it('is valid when required and optional parameters are given', () => { + const config = { + service: { name: 'my-service', environment: 'my-environment' }, + settings: { + transaction_sample_rate: 0.5, + capture_body: 'foo', + transaction_max_spans: 10 + } + }; + + expect(isConfigValid(config)).toBe(true); + }); + + it('is invalid when required parameters are not given', () => { + const config = {}; + expect(isConfigValid(config)).toBe(false); + }); +}); + +function isConfigValid(config: any) { + return isRight(agentConfigurationIntakeRt.decode(config)); +} diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts new file mode 100644 index 00000000000000..32a2832b5eaf33 --- /dev/null +++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { transactionSampleRateRt } from '../transaction_sample_rate_rt'; +import { transactionMaxSpansRt } from '../transaction_max_spans_rt'; + +export const serviceRt = t.partial({ + name: t.string, + environment: t.string +}); + +export const agentConfigurationIntakeRt = t.intersection([ + t.partial({ agent_name: t.string }), + t.type({ + service: serviceRt, + settings: t.partial({ + transaction_sample_rate: transactionSampleRateRt, + capture_body: t.string, + transaction_max_spans: transactionMaxSpansRt + }) + }) +]); diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts index fb040afa172519..ebd954015c9102 100644 --- a/x-pack/plugins/apm/server/index.ts +++ b/x-pack/plugins/apm/server/index.ts @@ -15,6 +15,7 @@ export const config = { ui: true }, schema: schema.object({ + enabled: schema.boolean({ defaultValue: true }), serviceMapEnabled: schema.boolean({ defaultValue: false }), autocreateApmIndexPattern: schema.boolean({ defaultValue: true }), ui: schema.object({ diff --git a/x-pack/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/plugins/apm/server/lib/helpers/es_client.ts index 8ada02d085631b..c22084dbb71681 100644 --- a/x-pack/plugins/apm/server/lib/helpers/es_client.ts +++ b/x-pack/plugins/apm/server/lib/helpers/es_client.ts @@ -7,12 +7,14 @@ /* eslint-disable no-console */ import { IndexDocumentParams, - IndicesCreateParams, IndicesDeleteParams, - SearchParams + SearchParams, + IndicesCreateParams, + DeleteDocumentResponse } from 'elasticsearch'; -import { cloneDeep, isString, merge, uniqueId } from 'lodash'; +import { cloneDeep, isString, merge } from 'lodash'; import { KibanaRequest } from 'src/core/server'; +import chalk from 'chalk'; import { ESSearchRequest, ESSearchResponse @@ -125,6 +127,10 @@ interface ClientCreateOptions { export type ESClient = ReturnType; +function formatObj(obj: Record) { + return JSON.stringify(obj, null, 2); +} + export function getESClient( context: APMRequestHandlerContext, request: KibanaRequest, @@ -135,25 +141,49 @@ export function getESClient( callAsInternalUser } = context.core.elasticsearch.dataClient; - const callMethod = clientAsInternalUser - ? callAsInternalUser - : callAsCurrentUser; + async function callEs(operationName: string, params: Record) { + const startTime = process.hrtime(); + + let res: any; + let esError = null; + try { + res = clientAsInternalUser + ? await callAsInternalUser(operationName, params) + : await callAsCurrentUser(operationName, params); + } catch (e) { + // catch error and throw after outputting debug info + esError = e; + } - const debug = context.params.query._debug; + if (context.params.query._debug) { + const highlightColor = esError ? 'bgRed' : 'inverse'; + const diff = process.hrtime(startTime); + const duration = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`; + const routeInfo = `${request.route.method.toUpperCase()} ${ + request.route.path + }`; - function withTime( - fn: (log: typeof console.log) => Promise - ): Promise { - const log = console.log.bind(console, uniqueId()); - if (!debug) { - return fn(log); + console.log( + chalk.bold[highlightColor](`=== Debug: ${routeInfo} (${duration}) ===`) + ); + + if (operationName === 'search') { + console.log(`GET ${params.index}/_${operationName}`); + console.log(formatObj(params.body)); + } else { + console.log(chalk.bold('ES operation:'), operationName); + + console.log(chalk.bold('ES query:')); + console.log(formatObj(params)); + } + console.log(`\n`); } - const time = process.hrtime(); - return fn(log).then(data => { - const now = process.hrtime(time); - log(`took: ${Math.round(now[0] * 1000 + now[1] / 1e6)}ms`); - return data; - }); + + if (esError) { + throw esError; + } + + return res; } return { @@ -170,40 +200,25 @@ export function getESClient( apmOptions ); - return withTime(log => { - if (context.params.query._debug) { - log(`--DEBUG ES QUERY--`); - log( - `${request.url.pathname} ${JSON.stringify(context.params.query)}` - ); - log(`GET ${nextParams.index}/_search`); - log(JSON.stringify(nextParams.body, null, 2)); - } - - return (callMethod('search', nextParams) as unknown) as Promise< - ESSearchResponse - >; - }); + return callEs('search', nextParams); }, index: (params: APMIndexDocumentParams) => { - return withTime(() => callMethod('index', params)); + return callEs('index', params); }, - delete: (params: IndicesDeleteParams) => { - return withTime(() => callMethod('delete', params)); + delete: (params: IndicesDeleteParams): Promise => { + return callEs('delete', params); }, indicesCreate: (params: IndicesCreateParams) => { - return withTime(() => callMethod('indices.create', params)); + return callEs('indices.create', params); }, hasPrivileges: ( params: IndexPrivilegesParams ): Promise => { - return withTime(() => - callMethod('transport.request', { - method: 'POST', - path: '/_security/user/_has_privileges', - body: params - }) - ); + return callEs('transport.request', { + method: 'POST', + path: '/_security/user/_has_privileges', + body: params + }); } }; } diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts index 7120d3bca6c259..ccd8b123e23e2a 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts @@ -58,8 +58,8 @@ export async function getServiceNodeMetadata({ const response = await client.search(query); return { - host: response.aggregations?.host.buckets[0].key || NOT_AVAILABLE_LABEL, + host: response.aggregations?.host.buckets[0]?.key || NOT_AVAILABLE_LABEL, containerId: - response.aggregations?.containerId.buckets[0].key || NOT_AVAILABLE_LABEL + response.aggregations?.containerId.buckets[0]?.key || NOT_AVAILABLE_LABEL }; } diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap index 542fdd99e26358..db34b4d5d20b5b 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap @@ -1,6 +1,90 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`agent configuration queries fetches all environments 1`] = ` +exports[`agent configuration queries findExactConfiguration find configuration by service.environment 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "service.name", + }, + }, + ], + }, + }, + Object { + "term": Object { + "service.environment": "bar", + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries findExactConfiguration find configuration by service.name 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "service.environment", + }, + }, + ], + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries findExactConfiguration find configuration by service.name and service.environment 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + Object { + "term": Object { + "service.environment": "bar", + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries getAllEnvironments fetches all environments 1`] = ` Object { "body": Object { "aggs": Object { @@ -41,14 +125,79 @@ Object { } `; -exports[`agent configuration queries fetches configurations 1`] = ` +exports[`agent configuration queries getExistingEnvironmentsForService fetches unavailable environments 1`] = ` +Object { + "body": Object { + "aggs": Object { + "environments": Object { + "terms": Object { + "field": "service.environment", + "missing": "ALL_OPTION_VALUE", + "size": 50, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + ], + }, + }, + "size": 0, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries getServiceNames fetches service names 1`] = ` +Object { + "body": Object { + "aggs": Object { + "services": Object { + "terms": Object { + "field": "service.name", + "size": 50, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "terms": Object { + "processor.event": Array [ + "transaction", + "error", + "metric", + ], + }, + }, + ], + }, + }, + "size": 0, + }, + "index": Array [ + "myIndex", + "myIndex", + "myIndex", + ], +} +`; + +exports[`agent configuration queries listConfigurations fetches configurations 1`] = ` Object { "index": "myIndex", "size": 200, } `; -exports[`agent configuration queries fetches filtered configurations with an environment 1`] = ` +exports[`agent configuration queries searchConfigurations fetches filtered configurations with an environment 1`] = ` Object { "body": Object { "query": Object { @@ -60,9 +209,7 @@ Object { "boost": 2, "filter": Object { "term": Object { - "service.name": Object { - "value": "foo", - }, + "service.name": "foo", }, }, }, @@ -72,9 +219,7 @@ Object { "boost": 1, "filter": Object { "term": Object { - "service.environment": Object { - "value": "bar", - }, + "service.environment": "bar", }, }, }, @@ -109,7 +254,7 @@ Object { } `; -exports[`agent configuration queries fetches filtered configurations without an environment 1`] = ` +exports[`agent configuration queries searchConfigurations fetches filtered configurations without an environment 1`] = ` Object { "body": Object { "query": Object { @@ -121,9 +266,7 @@ Object { "boost": 2, "filter": Object { "term": Object { - "service.name": Object { - "value": "foo", - }, + "service.name": "foo", }, }, }, @@ -157,68 +300,3 @@ Object { "index": "myIndex", } `; - -exports[`agent configuration queries fetches service names 1`] = ` -Object { - "body": Object { - "aggs": Object { - "services": Object { - "terms": Object { - "field": "service.name", - "size": 50, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "terms": Object { - "processor.event": Array [ - "transaction", - "error", - "metric", - ], - }, - }, - ], - }, - }, - "size": 0, - }, - "index": Array [ - "myIndex", - "myIndex", - "myIndex", - ], -} -`; - -exports[`agent configuration queries fetches unavailable environments 1`] = ` -Object { - "body": Object { - "aggs": Object { - "environments": Object { - "terms": Object { - "field": "service.environment", - "missing": "ALL_OPTION_VALUE", - "size": 50, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "foo", - }, - }, - ], - }, - }, - "size": 0, - }, - "index": "myIndex", -} -`; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts index ea8f50c90c1d3a..ddbe6892c54414 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts @@ -4,18 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface AgentConfiguration { +import t from 'io-ts'; +import { agentConfigurationIntakeRt } from '../../../../common/runtime_types/agent_configuration_intake_rt'; + +export type AgentConfigurationIntake = t.TypeOf< + typeof agentConfigurationIntakeRt +>; +export type AgentConfiguration = { '@timestamp': number; applied_by_agent?: boolean; etag?: string; agent_name?: string; - service: { - name?: string; - environment?: string; - }; - settings: { - transaction_sample_rate?: number; - capture_body?: string; - transaction_max_spans?: number; - }; -} +} & AgentConfigurationIntake; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts index 5a67f78de6f652..74fcc61dde863c 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts @@ -6,19 +6,19 @@ import hash from 'object-hash'; import { Setup } from '../../helpers/setup_request'; -import { AgentConfiguration } from './configuration_types'; +import { + AgentConfiguration, + AgentConfigurationIntake +} from './configuration_types'; import { APMIndexDocumentParams } from '../../helpers/es_client'; export async function createOrUpdateConfiguration({ configurationId, - configuration, + configurationIntake, setup }: { configurationId?: string; - configuration: Omit< - AgentConfiguration, - '@timestamp' | 'applied_by_agent' | 'etag' - >; + configurationIntake: AgentConfigurationIntake; setup: Setup; }) { const { internalClient, indices } = setup; @@ -27,15 +27,15 @@ export async function createOrUpdateConfiguration({ refresh: true, index: indices.apmAgentConfigurationIndex, body: { - agent_name: configuration.agent_name, + agent_name: configurationIntake.agent_name, service: { - name: configuration.service.name, - environment: configuration.service.environment + name: configurationIntake.service.name, + environment: configurationIntake.service.environment }, - settings: configuration.settings, + settings: configurationIntake.settings, '@timestamp': Date.now(), applied_by_agent: false, - etag: hash(configuration) + etag: hash(configurationIntake) } }; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts new file mode 100644 index 00000000000000..eea409882f8763 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + SERVICE_NAME, + SERVICE_ENVIRONMENT +} from '../../../../common/elasticsearch_fieldnames'; +import { Setup } from '../../helpers/setup_request'; +import { AgentConfiguration } from './configuration_types'; +import { ESSearchHit } from '../../../../typings/elasticsearch'; + +export async function findExactConfiguration({ + service, + setup +}: { + service: AgentConfiguration['service']; + setup: Setup; +}) { + const { internalClient, indices } = setup; + + const serviceNameFilter = service.name + ? { term: { [SERVICE_NAME]: service.name } } + : { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } }; + + const environmentFilter = service.environment + ? { term: { [SERVICE_ENVIRONMENT]: service.environment } } + : { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } }; + + const params = { + index: indices.apmAgentConfigurationIndex, + body: { + query: { + bool: { filter: [serviceNameFilter, environmentFilter] } + } + } + }; + + const resp = await internalClient.search( + params + ); + + return resp.hits.hits[0] as ESSearchHit | undefined; +} diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts index dccf8b110d082b..a9af1f6174fd51 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts @@ -48,8 +48,6 @@ export async function getAgentNameByService({ }; const { aggregations } = await client.search(params); - const agentName = aggregations?.agent_names.buckets[0].key as - | string - | undefined; - return { agentName }; + const agentName = aggregations?.agent_names.buckets[0]?.key; + return agentName as string | undefined; } diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts index a82d148781ad88..b951b7f350eed4 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts @@ -8,11 +8,12 @@ import { getAllEnvironments } from './get_environments/get_all_environments'; import { getExistingEnvironmentsForService } from './get_environments/get_existing_environments_for_service'; import { getServiceNames } from './get_service_names'; import { listConfigurations } from './list_configurations'; -import { searchConfigurations } from './search'; +import { searchConfigurations } from './search_configurations'; import { SearchParamsMock, inspectSearchParams } from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +import { findExactConfiguration } from './find_exact_configuration'; describe('agent configuration queries', () => { let mock: SearchParamsMock; @@ -21,68 +22,117 @@ describe('agent configuration queries', () => { mock.teardown(); }); - it('fetches all environments', async () => { - mock = await inspectSearchParams(setup => - getAllEnvironments({ - serviceName: 'foo', - setup - }) - ); + describe('getAllEnvironments', () => { + it('fetches all environments', async () => { + mock = await inspectSearchParams(setup => + getAllEnvironments({ + serviceName: 'foo', + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches unavailable environments', async () => { - mock = await inspectSearchParams(setup => - getExistingEnvironmentsForService({ - serviceName: 'foo', - setup - }) - ); + describe('getExistingEnvironmentsForService', () => { + it('fetches unavailable environments', async () => { + mock = await inspectSearchParams(setup => + getExistingEnvironmentsForService({ + serviceName: 'foo', + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches service names', async () => { - mock = await inspectSearchParams(setup => - getServiceNames({ - setup - }) - ); + describe('getServiceNames', () => { + it('fetches service names', async () => { + mock = await inspectSearchParams(setup => + getServiceNames({ + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches configurations', async () => { - mock = await inspectSearchParams(setup => - listConfigurations({ - setup - }) - ); + describe('listConfigurations', () => { + it('fetches configurations', async () => { + mock = await inspectSearchParams(setup => + listConfigurations({ + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches filtered configurations without an environment', async () => { - mock = await inspectSearchParams(setup => - searchConfigurations({ - serviceName: 'foo', - setup - }) - ); + describe('searchConfigurations', () => { + it('fetches filtered configurations without an environment', async () => { + mock = await inspectSearchParams(setup => + searchConfigurations({ + service: { + name: 'foo' + }, + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); + + it('fetches filtered configurations with an environment', async () => { + mock = await inspectSearchParams(setup => + searchConfigurations({ + service: { + name: 'foo', + environment: 'bar' + }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches filtered configurations with an environment', async () => { - mock = await inspectSearchParams(setup => - searchConfigurations({ - serviceName: 'foo', - environment: 'bar', - setup - }) - ); + describe('findExactConfiguration', () => { + it('find configuration by service.name', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { name: 'foo' }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); + + it('find configuration by service.environment', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { environment: 'bar' }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); + + it('find configuration by service.name and service.environment', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { name: 'foo', environment: 'bar' }, + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); }); diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts similarity index 68% rename from x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts index 766baead006b6e..9bbdc96a3a7974 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts @@ -12,29 +12,39 @@ import { Setup } from '../../helpers/setup_request'; import { AgentConfiguration } from './configuration_types'; export async function searchConfigurations({ - serviceName, - environment, + service, setup }: { - serviceName: string; - environment?: string; + service: AgentConfiguration['service']; setup: Setup; }) { const { internalClient, indices } = setup; - const environmentFilter = environment + + // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring). + // Additionally a boost has been added to service.name to ensure it scores higher. + // If there is tie between a config with a matching service.name and a config with a matching environment, the config that matches service.name wins + const serviceNameFilter = service.name + ? [ + { + constant_score: { + filter: { term: { [SERVICE_NAME]: service.name } }, + boost: 2 + } + } + ] + : []; + + const environmentFilter = service.environment ? [ { constant_score: { - filter: { term: { [SERVICE_ENVIRONMENT]: { value: environment } } }, + filter: { term: { [SERVICE_ENVIRONMENT]: service.environment } }, boost: 1 } } ] : []; - // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring) - // Additionally a boost has been added to service.name to ensure it scores higher - // if there is tie between a config with a matching service.name and a config with a matching environment const params = { index: indices.apmAgentConfigurationIndex, body: { @@ -42,12 +52,7 @@ export async function searchConfigurations({ bool: { minimum_should_match: 2, should: [ - { - constant_score: { - filter: { term: { [SERVICE_NAME]: { value: serviceName } } }, - boost: 2 - } - }, + ...serviceNameFilter, ...environmentFilter, { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } }, { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } } diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index f65e271389938a..21392edbb2c48f 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -23,11 +23,10 @@ import { import { agentConfigurationRoute, agentConfigurationSearchRoute, - createAgentConfigurationRoute, deleteAgentConfigurationRoute, listAgentConfigurationEnvironmentsRoute, listAgentConfigurationServicesRoute, - updateAgentConfigurationRoute, + createOrUpdateAgentConfigurationRoute, agentConfigurationAgentNameRoute } from './settings/agent_configuration'; import { @@ -83,11 +82,10 @@ const createApmApi = () => { .add(agentConfigurationAgentNameRoute) .add(agentConfigurationRoute) .add(agentConfigurationSearchRoute) - .add(createAgentConfigurationRoute) .add(deleteAgentConfigurationRoute) .add(listAgentConfigurationEnvironmentsRoute) .add(listAgentConfigurationServicesRoute) - .add(updateAgentConfigurationRoute) + .add(createOrUpdateAgentConfigurationRoute) // APM indices .add(apmIndexSettingsRoute) diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts index ddd6a270251310..83b845b1fc4365 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts @@ -9,15 +9,19 @@ import Boom from 'boom'; import { setupRequest } from '../../lib/helpers/setup_request'; import { getServiceNames } from '../../lib/settings/agent_configuration/get_service_names'; import { createOrUpdateConfiguration } from '../../lib/settings/agent_configuration/create_or_update_configuration'; -import { searchConfigurations } from '../../lib/settings/agent_configuration/search'; +import { searchConfigurations } from '../../lib/settings/agent_configuration/search_configurations'; +import { findExactConfiguration } from '../../lib/settings/agent_configuration/find_exact_configuration'; import { listConfigurations } from '../../lib/settings/agent_configuration/list_configurations'; import { getEnvironments } from '../../lib/settings/agent_configuration/get_environments'; import { deleteConfiguration } from '../../lib/settings/agent_configuration/delete_configuration'; import { createRoute } from '../create_route'; -import { transactionSampleRateRt } from '../../../common/runtime_types/transaction_sample_rate_rt'; -import { transactionMaxSpansRt } from '../../../common/runtime_types/transaction_max_spans_rt'; import { getAgentNameByService } from '../../lib/settings/agent_configuration/get_agent_name_by_service'; import { markAppliedByAgent } from '../../lib/settings/agent_configuration/mark_applied_by_agent'; +import { + serviceRt, + agentConfigurationIntakeRt +} from '../../../common/runtime_types/agent_configuration_intake_rt'; +import { jsonRt } from '../../../common/runtime_types/json_rt'; // get list of configurations export const agentConfigurationRoute = createRoute(core => ({ @@ -31,20 +35,34 @@ export const agentConfigurationRoute = createRoute(core => ({ // delete configuration export const deleteAgentConfigurationRoute = createRoute(() => ({ method: 'DELETE', - path: '/api/apm/settings/agent-configuration/{configurationId}', + path: '/api/apm/settings/agent-configuration', options: { tags: ['access:apm', 'access:apm_write'] }, params: { - path: t.type({ - configurationId: t.string + body: t.type({ + service: serviceRt }) }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const { configurationId } = context.params.path; + const { service } = context.params.body; + + const config = await findExactConfiguration({ service, setup }); + if (!config) { + context.logger.info( + `Config was not found for ${service.name}/${service.environment}` + ); + + throw Boom.notFound(); + } + + context.logger.info( + `Deleting config ${service.name}/${service.environment} (${config._id})` + ); + return await deleteConfiguration({ - configurationId, + configurationId: config._id, setup }); } @@ -62,23 +80,6 @@ export const listAgentConfigurationServicesRoute = createRoute(() => ({ } })); -const agentPayloadRt = t.intersection([ - t.partial({ agent_name: t.string }), - t.type({ - service: t.intersection([ - t.partial({ name: t.string }), - t.partial({ environment: t.string }) - ]) - }), - t.type({ - settings: t.intersection([ - t.partial({ transaction_sample_rate: transactionSampleRateRt }), - t.partial({ capture_body: t.string }), - t.partial({ transaction_max_spans: transactionMaxSpansRt }) - ]) - }) -]); - // get environments for service export const listAgentConfigurationEnvironmentsRoute = createRoute(() => ({ path: '/api/apm/settings/agent-configuration/environments', @@ -102,55 +103,47 @@ export const agentConfigurationAgentNameRoute = createRoute(() => ({ const setup = await setupRequest(context, request); const { serviceName } = context.params.query; const agentName = await getAgentNameByService({ serviceName, setup }); - return agentName; + return { agentName }; } })); -export const createAgentConfigurationRoute = createRoute(() => ({ - method: 'POST', - path: '/api/apm/settings/agent-configuration/new', - params: { - body: agentPayloadRt - }, +export const createOrUpdateAgentConfigurationRoute = createRoute(() => ({ + method: 'PUT', + path: '/api/apm/settings/agent-configuration', options: { tags: ['access:apm', 'access:apm_write'] }, + params: { + query: t.partial({ overwrite: jsonRt.pipe(t.boolean) }), + body: agentConfigurationIntakeRt + }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const configuration = context.params.body; + const { body, query } = context.params; - // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764) - context.logger.info( - `Hitting: /api/apm/settings/agent-configuration/new with ${configuration.service.name}/${configuration.service.environment}` - ); - const res = await createOrUpdateConfiguration({ - configuration, + // if the config already exists, it is fetched and updated + // this is to avoid creating two configs with identical service params + const config = await findExactConfiguration({ + service: body.service, setup }); - context.logger.info(`Created agent configuration`); - return res; - } -})); + // if the config exists ?overwrite=true is required + if (config && !query.overwrite) { + throw Boom.badRequest( + `A configuration already exists for "${body.service.name}/${body.service.environment}. Use ?overwrite=true to overwrite the existing configuration.` + ); + } + + context.logger.info( + `${config ? 'Updating' : 'Creating'} config ${body.service.name}/${ + body.service.environment + }` + ); -export const updateAgentConfigurationRoute = createRoute(() => ({ - method: 'PUT', - path: '/api/apm/settings/agent-configuration/{configurationId}', - options: { - tags: ['access:apm', 'access:apm_write'] - }, - params: { - path: t.type({ - configurationId: t.string - }), - body: agentPayloadRt - }, - handler: async ({ context, request }) => { - const setup = await setupRequest(context, request); - const { configurationId } = context.params.path; return await createOrUpdateConfiguration({ - configurationId, - configuration: context.params.body, + configurationId: config?._id, + configurationIntake: body, setup }); } @@ -162,41 +155,33 @@ export const agentConfigurationSearchRoute = createRoute(core => ({ path: '/api/apm/settings/agent-configuration/search', params: { body: t.type({ - service: t.intersection([ - t.type({ name: t.string }), - t.partial({ environment: t.string }) - ]), + service: serviceRt, etag: t.string }) }, handler: async ({ context, request }) => { - const { body } = context.params; - - // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764) - context.logger.info( - `Hitting: /api/apm/settings/agent-configuration/search for ${body.service.name}/${body.service.environment}` - ); + const { service, etag } = context.params.body; const setup = await setupRequest(context, request); const config = await searchConfigurations({ - serviceName: body.service.name, - environment: body.service.environment, + service, setup }); if (!config) { context.logger.info( - `Config was not found for ${body.service.name}/${body.service.environment}` + `Config was not found for ${service.name}/${service.environment}` ); - throw new Boom('Not found', { statusCode: 404 }); + throw Boom.notFound(); } context.logger.info( - `Config was found for ${body.service.name}/${body.service.environment}` + `Config was found for ${service.name}/${service.environment}` ); // update `applied_by_agent` field if etags match - if (body.etag === config._source.etag && !config._source.applied_by_agent) { + // this happens in the background and doesn't block the response + if (etag === config._source.etag && !config._source.applied_by_agent) { markAppliedByAgent({ id: config._id, body: config._source, setup }); } diff --git a/x-pack/plugins/data_enhanced/public/index.ts b/x-pack/plugins/data_enhanced/public/index.ts index 93b6b7a9571824..927716aae97808 100644 --- a/x-pack/plugins/data_enhanced/public/index.ts +++ b/x-pack/plugins/data_enhanced/public/index.ts @@ -9,3 +9,5 @@ import { DataEnhancedPlugin, DataEnhancedSetup, DataEnhancedStart } from './plug export const plugin = () => new DataEnhancedPlugin(); export { DataEnhancedSetup, DataEnhancedStart }; + +export { ASYNC_SEARCH_STRATEGY, IAsyncSearchRequest, IAsyncSearchOptions } from './search'; diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index 14b5382bc85aaa..4fe27d400f45fd 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -8,6 +8,7 @@ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { setAutocompleteService } from './services'; import { setupKqlQuerySuggestionProvider, KUERY_LANGUAGE_NAME } from './autocomplete'; +import { ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider } from './search'; export interface DataEnhancedSetupDependencies { data: DataPublicPluginSetup; @@ -20,11 +21,14 @@ export type DataEnhancedSetup = ReturnType; export type DataEnhancedStart = ReturnType; export class DataEnhancedPlugin implements Plugin { - public setup(core: CoreSetup, plugins: DataEnhancedSetupDependencies) { - plugins.data.autocomplete.addQuerySuggestionProvider( + constructor() {} + + public setup(core: CoreSetup, { data }: DataEnhancedSetupDependencies) { + data.autocomplete.addQuerySuggestionProvider( KUERY_LANGUAGE_NAME, setupKqlQuerySuggestionProvider(core) ); + data.search.registerSearchStrategyProvider(ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider); } public start(core: CoreStart, plugins: DataEnhancedStartDependencies) { diff --git a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts new file mode 100644 index 00000000000000..95f2c9e4770642 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts @@ -0,0 +1,125 @@ +/* + * 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 { of } from 'rxjs'; +import { AbortController } from 'abort-controller'; +import { coreMock } from '../../../../../src/core/public/mocks'; +import { asyncSearchStrategyProvider } from './async_search_strategy'; +import { IAsyncSearchOptions } from './types'; +import { CoreStart } from 'kibana/public'; + +describe('Async search strategy', () => { + let mockCoreStart: MockedKeys; + const mockSearch = jest.fn(); + const mockRequest = { params: {}, serverStrategy: 'foo' }; + const mockOptions: IAsyncSearchOptions = { pollInterval: 0 }; + + beforeEach(() => { + mockCoreStart = coreMock.createStart(); + mockSearch.mockReset(); + }); + + it('only sends one request if the first response is complete', async () => { + mockSearch.mockReturnValueOnce(of({ id: 1, total: 1, loaded: 1 })); + + const asyncSearch = asyncSearchStrategyProvider({ + core: mockCoreStart, + getSearchStrategy: jest.fn().mockImplementation(() => { + return () => { + return { + search: mockSearch, + }; + }; + }), + }); + + await asyncSearch.search(mockRequest, mockOptions).toPromise(); + + expect(mockSearch.mock.calls[0][0]).toEqual(mockRequest); + expect(mockSearch.mock.calls[0][1]).toEqual({}); + expect(mockSearch).toBeCalledTimes(1); + }); + + it('stops polling when the response is complete', async () => { + mockSearch + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1 })) + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 })) + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 })); + + const asyncSearch = asyncSearchStrategyProvider({ + core: mockCoreStart, + getSearchStrategy: jest.fn().mockImplementation(() => { + return () => { + return { + search: mockSearch, + }; + }; + }), + }); + + expect(mockSearch).toBeCalledTimes(0); + + await asyncSearch.search(mockRequest, mockOptions).toPromise(); + + expect(mockSearch).toBeCalledTimes(2); + }); + + it('only sends the ID and server strategy after the first request', async () => { + mockSearch + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1 })) + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 })); + + const asyncSearch = asyncSearchStrategyProvider({ + core: mockCoreStart, + getSearchStrategy: jest.fn().mockImplementation(() => { + return () => { + return { + search: mockSearch, + }; + }; + }), + }); + + expect(mockSearch).toBeCalledTimes(0); + + await asyncSearch.search(mockRequest, mockOptions).toPromise(); + + expect(mockSearch).toBeCalledTimes(2); + expect(mockSearch.mock.calls[0][0]).toEqual(mockRequest); + expect(mockSearch.mock.calls[1][0]).toEqual({ id: 1, serverStrategy: 'foo' }); + }); + + it('sends a DELETE request and stops polling when the signal is aborted', async () => { + mockSearch + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1 })) + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 })) + .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 })); + + const asyncSearch = asyncSearchStrategyProvider({ + core: mockCoreStart, + getSearchStrategy: jest.fn().mockImplementation(() => { + return () => { + return { + search: mockSearch, + }; + }; + }), + }); + const abortController = new AbortController(); + const options = { ...mockOptions, signal: abortController.signal }; + + const promise = asyncSearch.search(mockRequest, options).toPromise(); + abortController.abort(); + + try { + await promise; + } catch (e) { + expect(e.name).toBe('AbortError'); + expect(mockSearch).toBeCalledTimes(1); + expect(mockCoreStart.http.delete).toBeCalled(); + } + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.ts new file mode 100644 index 00000000000000..fa5d677a53b2a7 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.ts @@ -0,0 +1,75 @@ +/* + * 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 { EMPTY, fromEvent, NEVER, Observable, throwError, timer } from 'rxjs'; +import { mergeMap, expand, takeUntil } from 'rxjs/operators'; +import { + IKibanaSearchResponse, + ISearchContext, + ISearchStrategy, + SYNC_SEARCH_STRATEGY, + TSearchStrategyProvider, +} from '../../../../../src/plugins/data/public'; +import { IAsyncSearchRequest, IAsyncSearchOptions } from './types'; + +export const ASYNC_SEARCH_STRATEGY = 'ASYNC_SEARCH_STRATEGY'; + +declare module '../../../../../src/plugins/data/public' { + export interface IRequestTypesMap { + [ASYNC_SEARCH_STRATEGY]: IAsyncSearchRequest; + } +} + +export const asyncSearchStrategyProvider: TSearchStrategyProvider = ( + context: ISearchContext +): ISearchStrategy => { + const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); + const { search } = syncStrategyProvider(context); + return { + search: ( + request: IAsyncSearchRequest, + { pollInterval = 1000, ...options }: IAsyncSearchOptions = {} + ): Observable => { + const { serverStrategy } = request; + let id: string | undefined = request.id; + + const aborted$ = options.signal + ? fromEvent(options.signal, 'abort').pipe( + mergeMap(() => { + // If we haven't received the response to the initial request, including the ID, then + // we don't need to send a follow-up request to delete this search. Otherwise, we + // send the follow-up request to delete this search, then throw an abort error. + if (id !== undefined) { + context.core.http.delete(`/internal/search/${request.serverStrategy}/${id}`); + } + + const error = new Error('Aborted'); + error.name = 'AbortError'; + return throwError(error); + }) + ) + : NEVER; + + return search(request, options).pipe( + expand(response => { + // If the response indicates it is complete, stop polling and complete the observable + if ((response.loaded ?? 0) >= (response.total ?? 0)) return EMPTY; + + id = response.id; + + // Delay by the given poll interval + return timer(pollInterval).pipe( + // Send future requests using just the ID from the response + mergeMap(() => { + return search({ id, serverStrategy }, options); + }) + ); + }), + takeUntil(aborted$) + ); + }, + }; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/index.ts b/x-pack/plugins/data_enhanced/public/search/index.ts new file mode 100644 index 00000000000000..a7729aeea56478 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider } from './async_search_strategy'; +export { IAsyncSearchRequest, IAsyncSearchOptions } from './types'; diff --git a/x-pack/plugins/data_enhanced/public/search/types.ts b/x-pack/plugins/data_enhanced/public/search/types.ts new file mode 100644 index 00000000000000..edaaf1b22654d4 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/types.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ISearchOptions, ISyncSearchRequest } from '../../../../../src/plugins/data/public'; + +export interface IAsyncSearchRequest extends ISyncSearchRequest { + /** + * The ID received from the response from the initial request + */ + id?: string; +} + +export interface IAsyncSearchOptions extends ISearchOptions { + /** + * The number of milliseconds to wait between receiving a response and sending another request + */ + pollInterval?: number; +} diff --git a/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx b/x-pack/plugins/drilldowns/public/actions/flyout_create_drilldown/index.tsx similarity index 61% rename from x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx rename to x-pack/plugins/drilldowns/public/actions/flyout_create_drilldown/index.tsx index 06f134b10a4b7d..0b9f54f51f61e4 100644 --- a/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx +++ b/x-pack/plugins/drilldowns/public/actions/flyout_create_drilldown/index.tsx @@ -10,11 +10,11 @@ import { CoreStart } from 'src/core/public'; import { Action } from '../../../../../../src/plugins/ui_actions/public'; import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public'; -import { FormCreateDrilldown } from '../../components/form_create_drilldown'; +import { FlyoutCreateDrilldown } from '../../components/flyout_create_drilldown'; export const OPEN_FLYOUT_ADD_DRILLDOWN = 'OPEN_FLYOUT_ADD_DRILLDOWN'; -interface ActionContext { +export interface FlyoutCreateDrilldownActionContext { embeddable: IEmbeddable; } @@ -22,29 +22,31 @@ export interface OpenFlyoutAddDrilldownParams { overlays: () => Promise; } -export class OpenFlyoutAddDrilldown implements Action { +export class FlyoutCreateDrilldownAction implements Action { public readonly type = OPEN_FLYOUT_ADD_DRILLDOWN; public readonly id = OPEN_FLYOUT_ADD_DRILLDOWN; - public order = 100; + public order = 5; constructor(protected readonly params: OpenFlyoutAddDrilldownParams) {} public getDisplayName() { - return i18n.translate('xpack.drilldowns.panel.openFlyoutAddDrilldown.displayName', { - defaultMessage: 'Add drilldown', + return i18n.translate('xpack.drilldowns.FlyoutCreateDrilldownAction.displayName', { + defaultMessage: 'Create Drilldown', }); } public getIconType() { - return 'empty'; + return 'plusInCircle'; } - public async isCompatible({ embeddable }: ActionContext) { + public async isCompatible({ embeddable }: FlyoutCreateDrilldownActionContext) { return true; } - public async execute({ embeddable }: ActionContext) { + public async execute(context: FlyoutCreateDrilldownActionContext) { const overlays = await this.params.overlays(); - overlays.openFlyout(toMountPoint()); + const handle = overlays.openFlyout( + toMountPoint( handle.close()} />) + ); } } diff --git a/x-pack/plugins/drilldowns/public/actions/index.ts b/x-pack/plugins/drilldowns/public/actions/index.ts index c0ca7fac22049b..ce235043b4ef6e 100644 --- a/x-pack/plugins/drilldowns/public/actions/index.ts +++ b/x-pack/plugins/drilldowns/public/actions/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './open_flyout_add_drilldown'; +export * from './flyout_create_drilldown'; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.story.tsx similarity index 91% rename from x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx rename to x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.story.tsx index afa82f5e74c16b..7a9e19342f27ce 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.story.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; import { storiesOf } from '@storybook/react'; -import { DrilldownHelloBar } from '..'; +import { DrilldownHelloBar } from '.'; storiesOf('components/DrilldownHelloBar', module).add('default', () => { return ; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx new file mode 100644 index 00000000000000..1ef714f7b86e2e --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.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 React from 'react'; + +export interface DrilldownHelloBarProps { + docsLink?: string; +} + +/** + * @todo https://github.com/elastic/kibana/issues/55311 + */ +export const DrilldownHelloBar: React.FC = ({ docsLink }) => { + return ( +
+

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

+ View docs +
+ ); +}; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx index 895a100df3ac50..f28c8cfa3a0595 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx @@ -4,24 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; - -export interface DrilldownHelloBarProps { - docsLink?: string; -} - -export const DrilldownHelloBar: React.FC = ({ docsLink }) => { - return ( -
-

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

- View docs - -
- ); -}; +export * from './drilldown_hello_bar'; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.story.tsx similarity index 91% rename from x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx rename to x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.story.tsx index dfdd9627ab5cd4..5627a5d6f4522b 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.story.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; import { storiesOf } from '@storybook/react'; -import { DrilldownPicker } from '..'; +import { DrilldownPicker } from '.'; storiesOf('components/DrilldownPicker', module).add('default', () => { return ; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.tsx new file mode 100644 index 00000000000000..3748fc666c81c5 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +// eslint-disable-next-line +export interface DrilldownPickerProps {} + +export const DrilldownPicker: React.FC = () => { + return ( + + ); +}; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx index 3748fc666c81c5..3be289fe6d46e8 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx @@ -4,18 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; - -// eslint-disable-next-line -export interface DrilldownPickerProps {} - -export const DrilldownPicker: React.FC = () => { - return ( - - ); -}; +export * from './drilldown_picker'; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.story.tsx b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.story.tsx new file mode 100644 index 00000000000000..4f024b7d9cd6a2 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.story.tsx @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/* eslint-disable no-console */ + +import * as React from 'react'; +import { EuiFlyout } from '@elastic/eui'; +import { storiesOf } from '@storybook/react'; +import { FlyoutCreateDrilldown } from '.'; + +storiesOf('components/FlyoutCreateDrilldown', module) + .add('default', () => { + return ; + }) + .add('open in flyout', () => { + return ( + + + + ); + }); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.tsx b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.tsx new file mode 100644 index 00000000000000..b45ac9197c7e06 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.tsx @@ -0,0 +1,34 @@ +/* + * 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 { EuiButton } from '@elastic/eui'; +import { FormCreateDrilldown } from '../form_create_drilldown'; +import { FlyoutFrame } from '../flyout_frame'; +import { txtCreateDrilldown } from './i18n'; +import { FlyoutCreateDrilldownActionContext } from '../../actions'; + +export interface FlyoutCreateDrilldownProps { + context: FlyoutCreateDrilldownActionContext; + onClose?: () => void; +} + +export const FlyoutCreateDrilldown: React.FC = ({ + context, + onClose, +}) => { + const footer = ( + {}} fill> + {txtCreateDrilldown} + + ); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/i18n.ts b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/i18n.ts new file mode 100644 index 00000000000000..ceabc6d3a9aa51 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/i18n.ts @@ -0,0 +1,14 @@ +/* + * 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 txtCreateDrilldown = i18n.translate( + 'xpack.drilldowns.components.FlyoutCreateDrilldown.CreateDrilldown', + { + defaultMessage: 'Create drilldown', + } +); diff --git a/x-pack/legacy/plugins/siem/server/graphql/anomalies/index.ts b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/index.ts similarity index 70% rename from x-pack/legacy/plugins/siem/server/graphql/anomalies/index.ts rename to x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/index.ts index 4bfd6be173105d..ce235043b4ef6e 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/anomalies/index.ts +++ b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { createAnomaliesResolvers } from './resolvers'; -export { anomaliesSchema } from './schema.gql'; +export * from './flyout_create_drilldown'; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.story.tsx b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.story.tsx new file mode 100644 index 00000000000000..2715637f6392fe --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.story.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable no-console */ + +import * as React from 'react'; +import { EuiFlyout, EuiButton } from '@elastic/eui'; +import { storiesOf } from '@storybook/react'; +import { FlyoutFrame } from '.'; + +storiesOf('components/FlyoutFrame', module) + .add('default', () => { + return test; + }) + .add('with title', () => { + return test; + }) + .add('with onClose', () => { + return console.log('onClose')}>test; + }) + .add('custom footer', () => { + return click me!}>test; + }) + .add('open in flyout', () => { + return ( + + Save} + onClose={() => console.log('onClose')} + > + test + + + ); + }); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.test.tsx b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.test.tsx new file mode 100644 index 00000000000000..b5fb52fcf5c18a --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.test.tsx @@ -0,0 +1,102 @@ +/* + * 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 { render } from 'react-dom'; +import { render as renderTestingLibrary, fireEvent } from '@testing-library/react'; +import { FlyoutFrame } from '.'; + +describe('', () => { + test('renders without crashing', () => { + const div = document.createElement('div'); + render(, div); + }); + + describe('[title=]', () => { + test('renders title in

tag', () => { + const div = document.createElement('div'); + render(, div); + + const title = div.querySelector('h1'); + expect(title?.textContent).toBe('foobar'); + }); + + test('title can be any react node', () => { + const div = document.createElement('div'); + render( + + foo bar + + } + />, + div + ); + + const title = div.querySelector('h1'); + expect(title?.innerHTML).toBe('foo bar'); + }); + }); + + describe('[footer=]', () => { + test('if [footer=] prop not provided, does not render footer', () => { + const div = document.createElement('div'); + render(, div); + + const footer = div.querySelector('[data-test-subj="flyoutFooter"]'); + expect(footer).toBe(null); + }); + + test('can render anything in footer', () => { + const div = document.createElement('div'); + render( + + a b + + } + />, + div + ); + + const footer = div.querySelector('[data-test-subj="flyoutFooter"]'); + expect(footer?.innerHTML).toBe('a b'); + }); + }); + + describe('[onClose=]', () => { + test('does not render close button if "onClose" prop is missing', () => { + const div = document.createElement('div'); + render(, div); + + const closeButton = div.querySelector('[data-test-subj="flyoutCloseButton"]'); + expect(closeButton).toBe(null); + }); + + test('renders close button if "onClose" prop is provided', () => { + const div = document.createElement('div'); + render( {}} />, div); + + const closeButton = div.querySelector('[data-test-subj="flyoutCloseButton"]'); + expect(closeButton).not.toBe(null); + }); + + test('calls onClose prop when close button clicked', async () => { + const onClose = jest.fn(); + const el = renderTestingLibrary(); + + const closeButton = el.queryByText('Close'); + + expect(onClose).toHaveBeenCalledTimes(0); + + fireEvent.click(closeButton!); + + expect(onClose).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx new file mode 100644 index 00000000000000..2945cfd739482d --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, +} from '@elastic/eui'; +import { txtClose } from './i18n'; + +export interface FlyoutFrameProps { + title?: React.ReactNode; + footer?: React.ReactNode; + onClose?: () => void; +} + +/** + * @todo This component can be moved to `kibana_react`. + */ +export const FlyoutFrame: React.FC = ({ + title = '', + footer, + onClose, + children, +}) => { + const headerFragment = title && ( + + +

{title}

+
+
+ ); + + const footerFragment = (onClose || footer) && ( + + + + {onClose && ( + + {txtClose} + + )} + + + {footer} + + + + ); + + return ( + <> + {headerFragment} + {children} + {footerFragment} + + ); +}; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/i18n.ts b/x-pack/plugins/drilldowns/public/components/flyout_frame/i18n.ts new file mode 100644 index 00000000000000..257d7d36dbee19 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/i18n.ts @@ -0,0 +1,11 @@ +/* + * 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 txtClose = i18n.translate('xpack.drilldowns.components.FlyoutFrame.Close', { + defaultMessage: 'Close', +}); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/index.tsx b/x-pack/plugins/drilldowns/public/components/flyout_frame/index.tsx new file mode 100644 index 00000000000000..040b4b6b5e2439 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/index.tsx @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './flyout_frame'; diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx deleted file mode 100644 index 34f6932b41dacf..00000000000000 --- a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import * as React from 'react'; -import { storiesOf } from '@storybook/react'; -import { FormCreateDrilldown } from '..'; - -storiesOf('components/FormCreateDrilldown', module).add('default', () => { - return ; -}); diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.story.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.story.tsx new file mode 100644 index 00000000000000..e7e1d67473e8c7 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.story.tsx @@ -0,0 +1,34 @@ +/* + * 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. + */ + +/* eslint-disable no-console */ + +import * as React from 'react'; +import { EuiFlyout } from '@elastic/eui'; +import { storiesOf } from '@storybook/react'; +import { FormCreateDrilldown } from '.'; + +const DemoEditName: React.FC = () => { + const [name, setName] = React.useState(''); + + return ; +}; + +storiesOf('components/FormCreateDrilldown', module) + .add('default', () => { + return ; + }) + .add('[name=foobar]', () => { + return ; + }) + .add('can edit name', () => ) + .add('open in flyout', () => { + return ( + + + + ); + }); diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.test.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.test.tsx new file mode 100644 index 00000000000000..6691966e47e647 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.test.tsx @@ -0,0 +1,68 @@ +/* + * 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 { render } from 'react-dom'; +import { FormCreateDrilldown } from '.'; +import { render as renderTestingLibrary, fireEvent } from '@testing-library/react'; +import { txtNameOfDrilldown } from './i18n'; + +describe('', () => { + test('renders without crashing', () => { + const div = document.createElement('div'); + render( {}} />, div); + }); + + describe('[name=]', () => { + test('if name not provided, uses to empty string', () => { + const div = document.createElement('div'); + + render(, div); + + const input = div.querySelector( + '[data-test-subj="dynamicActionNameInput"]' + ) as HTMLInputElement; + + expect(input?.value).toBe(''); + }); + + test('can set name input field value', () => { + const div = document.createElement('div'); + + render(, div); + + const input = div.querySelector( + '[data-test-subj="dynamicActionNameInput"]' + ) as HTMLInputElement; + + expect(input?.value).toBe('foo'); + + render(, div); + + expect(input?.value).toBe('bar'); + }); + + test('fires onNameChange callback on name change', () => { + const onNameChange = jest.fn(); + const utils = renderTestingLibrary( + + ); + const input = utils.getByLabelText(txtNameOfDrilldown); + + expect(onNameChange).toHaveBeenCalledTimes(0); + + fireEvent.change(input, { target: { value: 'qux' } }); + + expect(onNameChange).toHaveBeenCalledTimes(1); + expect(onNameChange).toHaveBeenCalledWith('qux'); + + fireEvent.change(input, { target: { value: 'quxx' } }); + + expect(onNameChange).toHaveBeenCalledTimes(2); + expect(onNameChange).toHaveBeenCalledWith('quxx'); + }); + }); +}); diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.tsx new file mode 100644 index 00000000000000..4422de604092bb --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.tsx @@ -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 React from 'react'; +import { EuiForm, EuiFormRow, EuiFieldText } from '@elastic/eui'; +import { DrilldownHelloBar } from '../drilldown_hello_bar'; +import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n'; +import { DrilldownPicker } from '../drilldown_picker'; + +const noop = () => {}; + +export interface FormCreateDrilldownProps { + name?: string; + onNameChange?: (name: string) => void; +} + +export const FormCreateDrilldown: React.FC = ({ + name = '', + onNameChange = noop, +}) => { + const nameFragment = ( + + onNameChange(event.target.value)} + data-test-subj="dynamicActionNameInput" + /> + + ); + + const triggerPicker =
Trigger Picker will be here
; + const actionPicker = ( + + + + ); + + return ( + <> + + {nameFragment} + {triggerPicker} + {actionPicker} + + ); +}; diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts index 922131ba4b9012..4c0e287935edd7 100644 --- a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts @@ -7,21 +7,21 @@ import { i18n } from '@kbn/i18n'; export const txtNameOfDrilldown = i18n.translate( - 'xpack.drilldowns.components.form_create_drilldown.nameOfDrilldown', + 'xpack.drilldowns.components.FormCreateDrilldown.nameOfDrilldown', { defaultMessage: 'Name of drilldown', } ); export const txtUntitledDrilldown = i18n.translate( - 'xpack.drilldowns.components.form_create_drilldown.untitledDrilldown', + 'xpack.drilldowns.components.FormCreateDrilldown.untitledDrilldown', { defaultMessage: 'Untitled drilldown', } ); export const txtDrilldownAction = i18n.translate( - 'xpack.drilldowns.components.form_create_drilldown.drilldownAction', + 'xpack.drilldowns.components.FormCreateDrilldown.drilldownAction', { defaultMessage: 'Drilldown action', } diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx index 40cd4cf2b210b8..c2c5a7e435b391 100644 --- a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx @@ -4,27 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiForm, EuiFormRow, EuiFieldText } from '@elastic/eui'; -import { DrilldownHelloBar } from '../drilldown_hello_bar'; -import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n'; -import { DrilldownPicker } from '../drilldown_picker'; - -// eslint-disable-next-line -export interface FormCreateDrilldownProps {} - -export const FormCreateDrilldown: React.FC = () => { - return ( -
- - - - - - - - - -
- ); -}; +export * from './form_create_drilldown'; diff --git a/x-pack/plugins/drilldowns/public/service/drilldown_service.ts b/x-pack/plugins/drilldowns/public/service/drilldown_service.ts index f22f4521816480..b1310ba003ff7a 100644 --- a/x-pack/plugins/drilldowns/public/service/drilldown_service.ts +++ b/x-pack/plugins/drilldowns/public/service/drilldown_service.ts @@ -5,17 +5,17 @@ */ import { CoreSetup } from 'src/core/public'; -import { OpenFlyoutAddDrilldown } from '../actions/open_flyout_add_drilldown'; +import { FlyoutCreateDrilldownAction } from '../actions'; import { DrilldownsSetupDependencies } from '../plugin'; export class DrilldownService { bootstrap(core: CoreSetup, { uiActions }: DrilldownsSetupDependencies) { - const actionOpenFlyoutAddDrilldown = new OpenFlyoutAddDrilldown({ + const actionFlyoutCreateDrilldown = new FlyoutCreateDrilldownAction({ overlays: async () => (await core.getStartServices())[0].overlays, }); - uiActions.registerAction(actionOpenFlyoutAddDrilldown); - uiActions.attachAction('CONTEXT_MENU_TRIGGER', actionOpenFlyoutAddDrilldown.id); + uiActions.registerAction(actionFlyoutCreateDrilldown); + uiActions.attachAction('CONTEXT_MENU_TRIGGER', actionFlyoutCreateDrilldown.id); } /** diff --git a/x-pack/plugins/drilldowns/scripts/storybook.js b/x-pack/plugins/drilldowns/scripts/storybook.js index 9b0f57746e5849..2bfd0eb1a8f19c 100644 --- a/x-pack/plugins/drilldowns/scripts/storybook.js +++ b/x-pack/plugins/drilldowns/scripts/storybook.js @@ -9,5 +9,5 @@ import { join } from 'path'; // eslint-disable-next-line require('@kbn/storybook').runStorybookCli({ name: 'drilldowns', - storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.examples.tsx')], + storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.story.tsx')], }); diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts index ecefd4bfa271ef..b61196439ee4fa 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -173,6 +173,7 @@ describe('createIndex', () => { await clusterClientAdapter.createIndex('foo'); expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.create', { index: 'foo', + body: {}, }); }); diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts index c74eeacc9bb19c..d585fd4f539b5c 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -94,9 +94,12 @@ export class ClusterClientAdapter { return result as boolean; } - public async createIndex(name: string): Promise { + public async createIndex(name: string, body: any = {}): Promise { try { - await this.callEs('indices.create', { index: name }); + await this.callEs('indices.create', { + index: name, + body, + }); } catch (err) { if (err.body?.error?.type !== 'resource_already_exists_exception') { throw new Error(`error creating initial index: ${err.message}`); diff --git a/x-pack/plugins/event_log/server/es/documents.test.ts b/x-pack/plugins/event_log/server/es/documents.test.ts index 7edca4b3943a65..c08d0ac978bc94 100644 --- a/x-pack/plugins/event_log/server/es/documents.test.ts +++ b/x-pack/plugins/event_log/server/es/documents.test.ts @@ -22,8 +22,7 @@ describe('getIndexTemplate()', () => { test('returns the correct details of the index template', () => { const indexTemplate = getIndexTemplate(esNames); - expect(indexTemplate.index_patterns).toEqual([esNames.indexPattern]); - expect(indexTemplate.aliases[esNames.alias]).toEqual({}); + expect(indexTemplate.index_patterns).toEqual([esNames.indexPatternWithVersion]); expect(indexTemplate.settings.number_of_shards).toBeGreaterThanOrEqual(0); expect(indexTemplate.settings.number_of_replicas).toBeGreaterThanOrEqual(0); expect(indexTemplate.settings['index.lifecycle.name']).toBe(esNames.ilmPolicy); diff --git a/x-pack/plugins/event_log/server/es/documents.ts b/x-pack/plugins/event_log/server/es/documents.ts index 09dd7383c4c5e3..982454e671008a 100644 --- a/x-pack/plugins/event_log/server/es/documents.ts +++ b/x-pack/plugins/event_log/server/es/documents.ts @@ -10,10 +10,7 @@ import mappings from '../../generated/mappings.json'; // returns the body of an index template used in an ES indices.putTemplate call export function getIndexTemplate(esNames: EsNames) { const indexTemplateBody: any = { - index_patterns: [esNames.indexPattern], - aliases: { - [esNames.alias]: {}, - }, + index_patterns: [esNames.indexPatternWithVersion], settings: { number_of_shards: 1, number_of_replicas: 1, diff --git a/x-pack/plugins/event_log/server/es/init.ts b/x-pack/plugins/event_log/server/es/init.ts index 7094277f7aa9fd..c67d6541ce0020 100644 --- a/x-pack/plugins/event_log/server/es/init.ts +++ b/x-pack/plugins/event_log/server/es/init.ts @@ -62,7 +62,13 @@ class EsInitializationSteps { async createInitialIndexIfNotExists(): Promise { const exists = await this.esContext.esAdapter.doesAliasExist(this.esContext.esNames.alias); if (!exists) { - await this.esContext.esAdapter.createIndex(this.esContext.esNames.initialIndex); + await this.esContext.esAdapter.createIndex(this.esContext.esNames.initialIndex, { + aliases: { + [this.esContext.esNames.alias]: { + is_write_index: true, + }, + }, + }); } } } diff --git a/x-pack/plugins/event_log/server/es/names.mock.ts b/x-pack/plugins/event_log/server/es/names.mock.ts index 7b013a0d263da8..268421235b4b2d 100644 --- a/x-pack/plugins/event_log/server/es/names.mock.ts +++ b/x-pack/plugins/event_log/server/es/names.mock.ts @@ -9,11 +9,12 @@ import { EsNames } from './names'; const createNamesMock = () => { const mock: jest.Mocked = { base: '.kibana', - alias: '.kibana-event-log', + alias: '.kibana-event-log-8.0.0', ilmPolicy: '.kibana-event-log-policy', indexPattern: '.kibana-event-log-*', - initialIndex: '.kibana-event-log-000001', - indexTemplate: '.kibana-event-log-template', + indexPatternWithVersion: '.kibana-event-log-8.0.0-*', + initialIndex: '.kibana-event-log-8.0.0-000001', + indexTemplate: '.kibana-event-log-8.0.0-template', }; return mock; }; diff --git a/x-pack/plugins/event_log/server/es/names.test.ts b/x-pack/plugins/event_log/server/es/names.test.ts index d88c4212df91ca..baefd756bb1ed0 100644 --- a/x-pack/plugins/event_log/server/es/names.test.ts +++ b/x-pack/plugins/event_log/server/es/names.test.ts @@ -6,15 +6,21 @@ import { getEsNames } from './names'; +jest.mock('../lib/../../../../package.json', () => ({ + version: '1.2.3', +})); + describe('getEsNames()', () => { test('works as expected', () => { const base = 'XYZ'; + const version = '1.2.3'; const esNames = getEsNames(base); expect(esNames.base).toEqual(base); - expect(esNames.alias).toEqual(`${base}-event-log`); + expect(esNames.alias).toEqual(`${base}-event-log-${version}`); expect(esNames.ilmPolicy).toEqual(`${base}-event-log-policy`); expect(esNames.indexPattern).toEqual(`${base}-event-log-*`); - expect(esNames.initialIndex).toEqual(`${base}-event-log-000001`); - expect(esNames.indexTemplate).toEqual(`${base}-event-log-template`); + expect(esNames.indexPatternWithVersion).toEqual(`${base}-event-log-${version}-*`); + expect(esNames.initialIndex).toEqual(`${base}-event-log-${version}-000001`); + expect(esNames.indexTemplate).toEqual(`${base}-event-log-${version}-template`); }); }); diff --git a/x-pack/plugins/event_log/server/es/names.ts b/x-pack/plugins/event_log/server/es/names.ts index be737d23625f14..d55d02a16fc9a7 100644 --- a/x-pack/plugins/event_log/server/es/names.ts +++ b/x-pack/plugins/event_log/server/es/names.ts @@ -4,25 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ -const EVENT_LOG_NAME_SUFFIX = '-event-log'; +import xPackage from '../../../../package.json'; + +const EVENT_LOG_NAME_SUFFIX = `-event-log`; +const EVENT_LOG_VERSION_SUFFIX = `-${xPackage.version}`; export interface EsNames { base: string; alias: string; ilmPolicy: string; indexPattern: string; + indexPatternWithVersion: string; initialIndex: string; indexTemplate: string; } export function getEsNames(baseName: string): EsNames { const eventLogName = `${baseName}${EVENT_LOG_NAME_SUFFIX}`; + const eventLogNameWithVersion = `${eventLogName}${EVENT_LOG_VERSION_SUFFIX}`; return { base: baseName, - alias: eventLogName, + alias: eventLogNameWithVersion, ilmPolicy: `${eventLogName}-policy`, indexPattern: `${eventLogName}-*`, - initialIndex: `${eventLogName}-000001`, - indexTemplate: `${eventLogName}-template`, + indexPatternWithVersion: `${eventLogNameWithVersion}-*`, + initialIndex: `${eventLogNameWithVersion}-000001`, + indexTemplate: `${eventLogNameWithVersion}-template`, }; } diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/constants.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/constants.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts similarity index 96% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts index e5f0b25d89c3e1..7e3e1fba9c44a6 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts @@ -12,10 +12,10 @@ import { TestBedConfig, findTestSubject, nextTick, -} from '../../../../../../test_utils'; -import { IndexManagementHome } from '../../../public/application/sections/home'; +} from '../../../../../test_utils'; +import { IndexManagementHome } from '../../../public/application/sections/home'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { BASE_PATH } from '../../../common/constants'; -import { indexManagementStore } from '../../../public/application/store'; +import { indexManagementStore } from '../../../public/application/store'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { Template } from '../../../common/types'; import { WithAppDependencies, services } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts similarity index 95% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts index 6dce4453a67f93..66021b531919a9 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts @@ -9,7 +9,7 @@ import { setup as templateCreateSetup } from './template_create.helpers'; import { setup as templateCloneSetup } from './template_clone.helpers'; import { setup as templateEditSetup } from './template_edit.helpers'; -export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../../test_utils'; +export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../test_utils'; export { setupEnvironment } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx similarity index 95% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index 0212efe1f322d3..1eaf7efd173954 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @kbn/eslint/no-restricted-paths */ import React from 'react'; import axios from 'axios'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; @@ -10,7 +11,7 @@ import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import { notificationServiceMock, docLinksServiceMock, -} from '../../../../../../../src/core/public/mocks'; +} from '../../../../../../src/core/public/mocks'; import { AppContextProvider } from '../../../public/application/app_context'; import { httpService } from '../../../public/application/services/http'; import { breadcrumbService } from '../../../public/application/services/breadcrumbs'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts similarity index 85% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts index cd1b67c08d9345..36498b99ba1435 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; +import { registerTestBed, TestBedConfig } from '../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateClone } from '../../../public/application/sections/template_clone'; +import { TemplateClone } from '../../../public/application/sections/template_clone'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { formSetup } from './template_form.helpers'; import { TEMPLATE_NAME } from './constants'; import { WithAppDependencies } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts similarity index 84% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts index 8e62bc25d6bd1d..14a44968a93c32 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; +import { registerTestBed, TestBedConfig } from '../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateCreate } from '../../../public/application/sections/template_create'; +import { TemplateCreate } from '../../../public/application/sections/template_create'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { formSetup, TestSubjects } from './template_form.helpers'; import { WithAppDependencies } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts similarity index 85% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts index cb1025234b48e9..af5fa8b79ecad6 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; +import { registerTestBed, TestBedConfig } from '../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateEdit } from '../../../public/application/sections/template_edit'; +import { TemplateEdit } from '../../../public/application/sections/template_edit'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { formSetup, TestSubjects } from './template_form.helpers'; import { TEMPLATE_NAME } from './constants'; import { WithAppDependencies } from './setup_environment'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts similarity index 99% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts index a7c87723b33fb8..134c67c278b220 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TestBed, SetupFunc, UnwrapPromise } from '../../../../../../test_utils'; +import { TestBed, SetupFunc, UnwrapPromise } from '../../../../../test_utils'; import { Template } from '../../../common/types'; import { nextTick } from './index'; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/home.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/home.test.ts rename to x-pack/plugins/index_management/__jest__/client_integration/home.test.ts diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_clone.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_clone.test.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_create.test.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_edit.test.tsx rename to x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx diff --git a/x-pack/legacy/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap b/x-pack/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap rename to x-pack/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap diff --git a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js b/x-pack/plugins/index_management/__jest__/components/index_table.test.js similarity index 98% rename from x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js rename to x-pack/plugins/index_management/__jest__/components/index_table.test.js index 2b4fd894364580..15c3ef0b845624 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js +++ b/x-pack/plugins/index_management/__jest__/components/index_table.test.js @@ -20,13 +20,13 @@ import { setUiMetricService } from '../../public/application/services/api'; import { indexManagementStore } from '../../public/application/store'; import { setExtensionsService } from '../../public/application/store/selectors'; import { BASE_PATH, API_BASE_PATH } from '../../common/constants'; -import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers'; +import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; import { ExtensionsService } from '../../public/services'; import sinon from 'sinon'; import { findTestSubject } from '@elastic/eui/lib/test'; /* eslint-disable @kbn/eslint/no-restricted-paths */ -import { notificationServiceMock } from '../../../../../../src/core/public/notifications/notifications_service.mock'; +import { notificationServiceMock } from '../../../../../src/core/public/notifications/notifications_service.mock'; jest.mock('ui/new_platform'); diff --git a/x-pack/legacy/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap b/x-pack/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap rename to x-pack/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap diff --git a/x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js b/x-pack/plugins/index_management/__jest__/lib/flatten_object.test.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js rename to x-pack/plugins/index_management/__jest__/lib/flatten_object.test.js diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ace.js b/x-pack/plugins/index_management/__mocks__/ace.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__mocks__/ace.js rename to x-pack/plugins/index_management/__mocks__/ace.js diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ui/documentation_links.js b/x-pack/plugins/index_management/__mocks__/ui/documentation_links.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__mocks__/ui/documentation_links.js rename to x-pack/plugins/index_management/__mocks__/ui/documentation_links.js diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ui/notify.js b/x-pack/plugins/index_management/__mocks__/ui/notify.js similarity index 100% rename from x-pack/legacy/plugins/index_management/__mocks__/ui/notify.js rename to x-pack/plugins/index_management/__mocks__/ui/notify.js diff --git a/x-pack/legacy/plugins/index_management/common/constants/api_base_path.ts b/x-pack/plugins/index_management/common/constants/api_base_path.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/api_base_path.ts rename to x-pack/plugins/index_management/common/constants/api_base_path.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/base_path.ts b/x-pack/plugins/index_management/common/constants/base_path.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/base_path.ts rename to x-pack/plugins/index_management/common/constants/base_path.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/index.ts rename to x-pack/plugins/index_management/common/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/index_statuses.ts b/x-pack/plugins/index_management/common/constants/index_statuses.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/index_statuses.ts rename to x-pack/plugins/index_management/common/constants/index_statuses.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/invalid_characters.ts b/x-pack/plugins/index_management/common/constants/invalid_characters.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/invalid_characters.ts rename to x-pack/plugins/index_management/common/constants/invalid_characters.ts diff --git a/x-pack/legacy/plugins/index_management/common/constants/plugin.ts b/x-pack/plugins/index_management/common/constants/plugin.ts similarity index 86% rename from x-pack/legacy/plugins/index_management/common/constants/plugin.ts rename to x-pack/plugins/index_management/common/constants/plugin.ts index 2cd137f62d3dbf..e25f537edcf8da 100644 --- a/x-pack/legacy/plugins/index_management/common/constants/plugin.ts +++ b/x-pack/plugins/index_management/common/constants/plugin.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LicenseType } from '../../../../../plugins/licensing/common/types'; +import { LicenseType } from '../../../licensing/common/types'; const basicLicense: LicenseType = 'basic'; diff --git a/x-pack/legacy/plugins/index_management/common/constants/ui_metric.ts b/x-pack/plugins/index_management/common/constants/ui_metric.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/constants/ui_metric.ts rename to x-pack/plugins/index_management/common/constants/ui_metric.ts diff --git a/x-pack/legacy/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/index.ts rename to x-pack/plugins/index_management/common/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/lib/index.ts b/x-pack/plugins/index_management/common/lib/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/lib/index.ts rename to x-pack/plugins/index_management/common/lib/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/lib/template_serialization.ts rename to x-pack/plugins/index_management/common/lib/template_serialization.ts diff --git a/x-pack/legacy/plugins/index_management/common/types/index.ts b/x-pack/plugins/index_management/common/types/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/types/index.ts rename to x-pack/plugins/index_management/common/types/index.ts diff --git a/x-pack/legacy/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/common/types/templates.ts rename to x-pack/plugins/index_management/common/types/templates.ts diff --git a/x-pack/plugins/index_management/kibana.json b/x-pack/plugins/index_management/kibana.json new file mode 100644 index 00000000000000..7387a042988c01 --- /dev/null +++ b/x-pack/plugins/index_management/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "indexManagement", + "version": "kibana", + "server": true, + "ui": true, + "requiredPlugins": [ + "home", + "licensing", + "management" + ], + "optionalPlugins": [ + "usageCollection" + ], + "configPath": ["xpack", "index_management"] +} diff --git a/x-pack/legacy/plugins/index_management/public/application/app.tsx b/x-pack/plugins/index_management/public/application/app.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/app.tsx rename to x-pack/plugins/index_management/public/application/app.tsx index 3b475f3baba04a..83997dd6ece184 100644 --- a/x-pack/legacy/plugins/index_management/public/application/app.tsx +++ b/x-pack/plugins/index_management/public/application/app.tsx @@ -16,7 +16,7 @@ import { useServices } from './app_context'; export const App = () => { const { uiMetricService } = useServices(); - useEffect(() => uiMetricService.trackMetric('loaded', UIM_APP_LOAD), []); + useEffect(() => uiMetricService.trackMetric('loaded', UIM_APP_LOAD), [uiMetricService]); return ( diff --git a/x-pack/legacy/plugins/index_management/public/application/app_context.tsx b/x-pack/plugins/index_management/public/application/app_context.tsx similarity index 90% rename from x-pack/legacy/plugins/index_management/public/application/app_context.tsx rename to x-pack/plugins/index_management/public/application/app_context.tsx index 12e0d362a29302..2bb618ad8efceb 100644 --- a/x-pack/legacy/plugins/index_management/public/application/app_context.tsx +++ b/x-pack/plugins/index_management/public/application/app_context.tsx @@ -5,9 +5,9 @@ */ import React, { createContext, useContext } from 'react'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart } from '../../../../../src/core/public'; -import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/public'; +import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public'; import { IndexMgmtMetricsType } from '../types'; import { UiMetricService, NotificationService, HttpService } from './services'; import { ExtensionsService } from '../services'; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/index.ts b/x-pack/plugins/index_management/public/application/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/index.ts rename to x-pack/plugins/index_management/public/application/components/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts similarity index 90% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts index e3313bfba56fd9..6d64cb73da4bdd 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts @@ -10,7 +10,7 @@ export { getRandomString, findTestSubject, TestBed, -} from '../../../../../../../../../../test_utils'; +} from '../../../../../../../../../test_utils'; export const componentHelpers = { mappingsEditor: { setup: mappingsEditorSetup }, diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts similarity index 85% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts index dcee956130a29f..acb416654023eb 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed } from '../../../../../../../../../../test_utils'; +import { registerTestBed } from '../../../../../../../../../test_utils'; import { MappingsEditor } from '../../../mappings_editor'; export const setup = (props: any) => diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/chained_multifields_warning.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/chained_multifields_warning.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/chained_multifields_warning.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/chained_multifields_warning.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx index 0c5c9e2a15b751..9b0b8420f9ea93 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx @@ -110,7 +110,7 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => { }); }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); useEffect(() => { if (didMountRef.current) { @@ -121,14 +121,14 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => { // Avoid reseting the form on component mount. didMountRef.current = true; } - }, [defaultValue]); + }, [defaultValue, form]); useEffect(() => { return () => { // On unmount => save in the state a snapshot of the current form data. dispatch({ type: 'configuration.save' }); }; - }, []); + }, [dispatch]); return (
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx similarity index 91% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx index 378d669dee69cb..400de4052afa4b 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx @@ -24,7 +24,7 @@ export const DocumentFields = React.memo(() => { if (editorType === 'json') { return deNormalize(fields); } - }, [editorType]); + }, [editorType, fields]); const editor = editorType === 'json' ? ( @@ -41,9 +41,12 @@ export const DocumentFields = React.memo(() => { return ; }; - const onSearchChange = useCallback((value: string) => { - dispatch({ type: 'search:update', value }); - }, []); + const onSearchChange = useCallback( + (value: string) => { + dispatch({ type: 'search:update', value }); + }, + [dispatch] + ); const searchTerm = search.term.trim(); diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx similarity index 86% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx index de3d70db31af4d..a91231352c1684 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx @@ -56,15 +56,21 @@ export const AnalyzerParameterSelects = ({ }); return subscription.unsubscribe; - }, [form]); + }, [form, onChange]); - const getSubOptionsMeta = (mainValue: string) => - mapOptionsToSubOptions !== undefined ? mapOptionsToSubOptions[mainValue] : undefined; + const getSubOptionsMeta = useCallback( + (mainValue: string) => + mapOptionsToSubOptions !== undefined ? mapOptionsToSubOptions[mainValue] : undefined, + [mapOptionsToSubOptions] + ); - const onMainValueChange = useCallback((mainValue: unknown) => { - const subOptionsMeta = getSubOptionsMeta(mainValue as string); - form.setFieldValue('sub', subOptionsMeta ? subOptionsMeta.options[0].value : undefined); - }, []); + const onMainValueChange = useCallback( + (mainValue: unknown) => { + const subOptionsMeta = getSubOptionsMeta(mainValue as string); + form.setFieldValue('sub', subOptionsMeta ? subOptionsMeta.options[0].value : undefined); + }, + [form, getSubOptionsMeta] + ); const renderSelect = (field: FieldHook, opts: Options) => { const isSuperSelect = areOptionsSuperSelect(opts); diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx similarity index 83% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx index 5f1b8c0df770ea..60b025ce644efe 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx @@ -66,7 +66,7 @@ export const CreateField = React.memo(function CreateFieldComponent({ }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); const cancel = () => { dispatch({ type: 'documentField.changeStatus', value: 'idle' }); @@ -108,43 +108,53 @@ export const CreateField = React.memo(function CreateFieldComponent({ * * @param type The selected field type */ - const getSubTypeMeta = ( - type: MainType - ): { subTypeLabel?: string; subTypeOptions?: ComboBoxOption[] } => { - const typeDefinition = TYPE_DEFINITION[type]; - const hasSubTypes = typeDefinition !== undefined && typeDefinition.subTypes; - - let subTypeOptions = hasSubTypes - ? typeDefinition - .subTypes!.types.map(subType => TYPE_DEFINITION[subType]) - .map( - subType => ({ value: subType.value as SubType, label: subType.label } as ComboBoxOption) - ) - : undefined; - - if (hasSubTypes) { - if (isMultiField) { - // If it is a multi-field, we need to filter out non-allowed types - subTypeOptions = filterTypesForMultiField(subTypeOptions!); - } else if (isRootLevelField === false) { - subTypeOptions = filterTypesForNonRootFields(subTypeOptions!); + const getSubTypeMeta = useCallback( + ( + type: MainType + ): { + subTypeLabel?: string; + subTypeOptions?: ComboBoxOption[]; + } => { + const typeDefinition = TYPE_DEFINITION[type]; + const hasSubTypes = typeDefinition !== undefined && typeDefinition.subTypes; + + let subTypeOptions = hasSubTypes + ? typeDefinition + .subTypes!.types.map(subType => TYPE_DEFINITION[subType]) + .map( + subType => + ({ value: subType.value as SubType, label: subType.label } as ComboBoxOption) + ) + : undefined; + + if (hasSubTypes) { + if (isMultiField) { + // If it is a multi-field, we need to filter out non-allowed types + subTypeOptions = filterTypesForMultiField(subTypeOptions!); + } else if (isRootLevelField === false) { + subTypeOptions = filterTypesForNonRootFields(subTypeOptions!); + } } - } - return { - subTypeOptions, - subTypeLabel: hasSubTypes ? typeDefinition.subTypes!.label : undefined, - }; - }; + return { + subTypeOptions, + subTypeLabel: hasSubTypes ? typeDefinition.subTypes!.label : undefined, + }; + }, + [isMultiField, isRootLevelField] + ); - const onTypeChange = (nextType: ComboBoxOption[]) => { - form.setFieldValue('type', nextType); + const onTypeChange = useCallback( + (nextType: ComboBoxOption[]) => { + form.setFieldValue('type', nextType); - if (nextType.length) { - const { subTypeOptions } = getSubTypeMeta(nextType[0].value as MainType); - form.setFieldValue('subType', subTypeOptions ? [subTypeOptions[0]] : undefined); - } - }; + if (nextType.length) { + const { subTypeOptions } = getSubTypeMeta(nextType[0].value as MainType); + form.setFieldValue('subType', subTypeOptions ? [subTypeOptions[0]] : undefined); + } + }, + [form, getSubTypeMeta] + ); const renderFormFields = useCallback( ({ type }) => { @@ -208,7 +218,7 @@ export const CreateField = React.memo(function CreateFieldComponent({ ); }, - [form, isMultiField] + [getSubTypeMeta, isMultiField, isRootLevelField, onTypeChange] ); const renderFormActions = () => ( diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx similarity index 97% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx index 284ae8803acb56..1f77584439568c 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx @@ -32,11 +32,11 @@ export const EditFieldContainer = React.memo(({ field, allFields }: Props) => { }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); const exitEdit = useCallback(() => { dispatch({ type: 'documentField.changeStatus', value: 'idle' }); - }, []); + }, [dispatch]); return ; }); diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx similarity index 94% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx index f2d1d4ebc9a561..57bb08a1935a56 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx @@ -23,7 +23,7 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop fields: { byId, maxNestedDepth }, } = useMappingsState(); - const getField = (id: string) => byId[id]; + const getField = useCallback((id: string) => byId[id], [byId]); const field: NormalizedField = getField(fieldId); const parentField: NormalizedField | undefined = @@ -44,7 +44,7 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop const childFieldsArray = useMemo( () => (childFields !== undefined ? childFields.map(getField) : []), - [childFields] + [childFields, getField] ); const addField = useCallback(() => { @@ -52,18 +52,18 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop type: 'documentField.createField', value: fieldId, }); - }, [fieldId]); + }, [fieldId, dispatch]); const editField = useCallback(() => { dispatch({ type: 'documentField.editField', value: fieldId, }); - }, [fieldId]); + }, [fieldId, dispatch]); const toggleExpand = useCallback(() => { dispatch({ type: 'field.toggleExpand', value: { fieldId } }); - }, [fieldId]); + }, [fieldId, dispatch]); return ( { documentFields: { status, fieldToAddFieldTo }, } = useMappingsState(); - const getField = (fieldId: string) => byId[fieldId]; - const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields]); + const getField = useCallback((fieldId: string) => byId[fieldId], [byId]); + const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields, getField]); - const addField = () => { + const addField = useCallback(() => { dispatch({ type: 'documentField.createField' }); - }; + }, [dispatch]); useEffect(() => { /** @@ -32,7 +32,7 @@ export const DocumentFieldsTreeEditor = () => { if (status === 'idle' && fields.length === 0) { addField(); } - }, [fields, status]); + }, [addField, fields, status]); const renderCreateField = () => { // The "fieldToAddFieldTo" is undefined when adding to the top level "properties" object. diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx similarity index 99% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx index 34647af38865d1..ed0d269e0a2a03 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx @@ -19,7 +19,7 @@ jest.mock('@elastic/eui', () => ({ ), })); -import { registerTestBed, nextTick, TestBed } from '../../../../../../../../../test_utils'; +import { registerTestBed, nextTick, TestBed } from '../../../../../../../../test_utils'; import { LoadMappingsProvider } from './load_mappings_provider'; const ComponentToTest = ({ onJson }: { onJson: () => void }) => ( diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx index 471217108ba6f5..f32fcb3956e1c3 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx @@ -69,7 +69,7 @@ export const TemplatesForm = React.memo(({ defaultValue }: Props) => { }); }); return subscription.unsubscribe; - }, [form]); + }, [form, dispatch]); useEffect(() => { if (didMountRef.current) { @@ -80,14 +80,14 @@ export const TemplatesForm = React.memo(({ defaultValue }: Props) => { // Avoid reseting the form on component mount. didMountRef.current = true; } - }, [defaultValue]); + }, [defaultValue, form]); useEffect(() => { return () => { // On unmount => save in the state a snapshot of the current form data. dispatch({ type: 'templates.save' }); }; - }, []); + }, [dispatch]); return ( <> diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx index a5a1153f3ba822..d4826d1b93b4e2 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx @@ -82,7 +82,7 @@ export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSetting isValid: true, }); } - }, [multipleMappingsDeclared]); + }, [multipleMappingsDeclared, onUpdate, defaultValue]); const changeTab = async (tab: TabName, state: State) => { if (selectedTab === 'advanced') { diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx rename to x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx index 634fde393c2cdb..9bd6e0428aacf6 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx @@ -176,7 +176,7 @@ export const MappingsState = React.memo( }, isValid: state.isValid, }); - }, [state]); + }, [mappingsType, onUpdate, state]); useEffect(() => { /** @@ -195,7 +195,7 @@ export const MappingsState = React.memo( } else { didMountRef.current = true; } - }, [defaultValue]); + }, [defaultValue, parsedFieldsDefaultValue]); return ( diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts similarity index 72% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts index e99d8840d57dfe..2979015c07455d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts @@ -24,7 +24,7 @@ export { VALIDATION_TYPES, ValidationFunc, ValidationFuncArg, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; +} from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; export { CheckBoxField, @@ -39,14 +39,14 @@ export { ComboBoxField, ToggleField, JsonEditorField, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/components'; +} from '../../../../../../../src/plugins/es_ui_shared/static/forms/components'; export { fieldFormatters, fieldValidators, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +} from '../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; export { JsonEditor, OnJsonEditorUpdateHandler, -} from '../../../../../../../../src/plugins/es_ui_shared/public'; +} from '../../../../../../../src/plugins/es_ui_shared/public'; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts rename to x-pack/plugins/index_management/public/application/components/mappings_editor/types.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts b/x-pack/plugins/index_management/public/application/components/no_match/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts rename to x-pack/plugins/index_management/public/application/components/no_match/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx b/x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx rename to x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts b/x-pack/plugins/index_management/public/application/components/page_error/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts rename to x-pack/plugins/index_management/public/application/components/page_error/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx b/x-pack/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx rename to x-pack/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx b/x-pack/plugins/index_management/public/application/components/section_error.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx rename to x-pack/plugins/index_management/public/application/components/section_error.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx b/x-pack/plugins/index_management/public/application/components/section_loading.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx rename to x-pack/plugins/index_management/public/application/components/section_loading.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx b/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx rename to x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts b/x-pack/plugins/index_management/public/application/components/template_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts rename to x-pack/plugins/index_management/public/application/components/template_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts rename to x-pack/plugins/index_management/public/application/components/template_form/steps/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx similarity index 94% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx index dd8d49a5690425..2f6e055b5d0c6d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx @@ -7,15 +7,8 @@ import React, { useEffect } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { - useForm, - Form, - getUseField, -} from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; -import { - getFormRow, - Field, -} from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/components'; + +import { useForm, Form, getUseField, getFormRow, Field } from '../../../../shared_imports'; import { documentationService } from '../../../services/documentation'; import { StepProps } from '../types'; import { schemas, nameConfig, nameConfigWithoutValidations } from '../template_form_schemas'; @@ -80,11 +73,11 @@ export const StepLogistics: React.FunctionComponent = ({ useEffect(() => { onStepValidityChange(form.isValid); - }, [form.isValid]); + }, [form.isValid, onStepValidityChange]); useEffect(() => { setDataGetter(form.submit); - }, [form]); + }, [form.submit, setDataGetter]); const { name, indexPatterns, order, version } = fieldsMeta; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx index 09172bf5cd0caa..09da43b83c3c51 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx @@ -20,7 +20,7 @@ import { EuiCodeBlock, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { serializers } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +import { serializers } from '../../../../shared_imports'; import { serializeTemplate } from '../../../../../common/lib/template_serialization'; import { Template } from '../../../../../common/types'; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts similarity index 83% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts rename to x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts index ae16a2e9263ff5..fbe479ea0cf235 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useState } from 'react'; +import { useEffect, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { isJSON } from '../../../../../../../../../src/plugins/es_ui_shared/static/validators/string'; +import { isJSON } from '../../../../shared_imports'; import { StepProps } from '../types'; interface Parameters { @@ -29,7 +29,7 @@ export const useJsonStep = ({ const [content, setContent] = useState(stringifyJson(defaultValue)); const [error, setError] = useState(null); - const validateContent = () => { + const validateContent = useCallback(() => { // We allow empty string as it will be converted to "{}"" const isValid = content.trim() === '' ? true : isJSON(content); if (!isValid) { @@ -42,20 +42,20 @@ export const useJsonStep = ({ setError(null); } return isValid; - }; + }, [content]); - const dataGetter = () => { + const dataGetter = useCallback(() => { const isValid = validateContent(); const value = isValid && content.trim() !== '' ? JSON.parse(content) : {}; const data = { [prop]: value }; return Promise.resolve({ isValid, data }); - }; + }, [content, validateContent, prop]); useEffect(() => { const isValid = validateContent(); onStepValidityChange(isValid); setDataGetter(dataGetter); - }, [content]); + }, [content, dataGetter, onStepValidityChange, setDataGetter, validateContent]); return { content, diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx similarity index 92% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx index 6a76e1d203b70f..58be9b2c633653 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, useState, useRef } from 'react'; +import React, { Fragment, useState, useRef, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, @@ -14,7 +14,7 @@ import { EuiSpacer, } from '@elastic/eui'; -import { serializers } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +import { serializers } from '../../../shared_imports'; import { Template } from '../../../../common/types'; import { TemplateSteps } from './template_steps'; import { StepAliases, StepLogistics, StepMappings, StepSettings, StepReview } from './steps'; @@ -70,19 +70,25 @@ export const TemplateForm: React.FunctionComponent = ({ const CurrentStepComponent = stepComponentMap[currentStep]; const isStepValid = validation[currentStep].isValid; - const setStepDataGetter = (stepDataGetter: DataGetterFunc) => { - stepsDataGetters.current[currentStep] = stepDataGetter; - }; + const setStepDataGetter = useCallback( + (stepDataGetter: DataGetterFunc) => { + stepsDataGetters.current[currentStep] = stepDataGetter; + }, + [currentStep] + ); - const onStepValidityChange = (isValid: boolean | undefined) => { - setValidation(prev => ({ - ...prev, - [currentStep]: { - isValid, - errors: {}, - }, - })); - }; + const onStepValidityChange = useCallback( + (isValid: boolean | undefined) => { + setValidation(prev => ({ + ...prev, + [currentStep]: { + isValid, + errors: {}, + }, + })); + }, + [currentStep] + ); const validateAndGetDataFromCurrentStep = async () => { const validateAndGetData = stepsDataGetters.current[currentStep]; diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx index ed2616cc64e381..9ff73b71adf50d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx @@ -13,12 +13,9 @@ import { FIELD_TYPES, VALIDATION_TYPES, FieldConfig, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; - -import { fieldFormatters, fieldValidators, -} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers'; +} from '../../../shared_imports'; import { INVALID_INDEX_PATTERN_CHARS, diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_steps.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx rename to x-pack/plugins/index_management/public/application/components/template_form/template_steps.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts b/x-pack/plugins/index_management/public/application/components/template_form/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts rename to x-pack/plugins/index_management/public/application/components/template_form/types.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts b/x-pack/plugins/index_management/public/application/constants/detail_panel_tabs.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts rename to x-pack/plugins/index_management/public/application/constants/detail_panel_tabs.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/index.ts b/x-pack/plugins/index_management/public/application/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/constants/index.ts rename to x-pack/plugins/index_management/public/application/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts b/x-pack/plugins/index_management/public/application/constants/refresh_intervals.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts rename to x-pack/plugins/index_management/public/application/constants/refresh_intervals.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/index.tsx b/x-pack/plugins/index_management/public/application/index.tsx similarity index 94% rename from x-pack/legacy/plugins/index_management/public/application/index.tsx rename to x-pack/plugins/index_management/public/application/index.tsx index b9859903f14345..5850cb8d42f1aa 100644 --- a/x-pack/legacy/plugins/index_management/public/application/index.tsx +++ b/x-pack/plugins/index_management/public/application/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart } from '../../../../../src/core/public'; import { AppContextProvider, AppDependencies } from './app_context'; import { App } from './app'; diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/ace.js b/x-pack/plugins/index_management/public/application/lib/ace.js similarity index 94% rename from x-pack/legacy/plugins/index_management/public/application/lib/ace.js rename to x-pack/plugins/index_management/public/application/lib/ace.js index b9620dfbdb1207..3b37c8fb8870ed 100644 --- a/x-pack/legacy/plugins/index_management/public/application/lib/ace.js +++ b/x-pack/plugins/index_management/public/application/lib/ace.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import ace from 'ace'; +import brace from 'brace'; import 'brace/ext/language_tools'; const splitTokens = line => { @@ -43,14 +43,14 @@ const wordCompleter = words => { }; export const createAceEditor = (div, value, readOnly = true, autocompleteArray) => { - const editor = ace.edit(div); + const editor = brace.edit(div); editor.$blockScrolling = Infinity; editor.setValue(value, -1); const session = editor.getSession(); session.setUseWrapMode(true); session.setMode('ace/mode/json'); if (autocompleteArray) { - const languageTools = ace.acequire('ace/ext/language_tools'); + const languageTools = brace.acequire('ace/ext/language_tools'); const autocompleter = wordCompleter(autocompleteArray); languageTools.setCompleters([autocompleter]); } diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js b/x-pack/plugins/index_management/public/application/lib/edit_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js rename to x-pack/plugins/index_management/public/application/lib/edit_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js b/x-pack/plugins/index_management/public/application/lib/flatten_object.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js rename to x-pack/plugins/index_management/public/application/lib/flatten_object.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js b/x-pack/plugins/index_management/public/application/lib/flatten_panel_tree.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js rename to x-pack/plugins/index_management/public/application/lib/flatten_panel_tree.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js b/x-pack/plugins/index_management/public/application/lib/index_status_labels.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js rename to x-pack/plugins/index_management/public/application/lib/index_status_labels.js diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts b/x-pack/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts rename to x-pack/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js b/x-pack/plugins/index_management/public/application/lib/render_badges.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js rename to x-pack/plugins/index_management/public/application/lib/render_badges.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx b/x-pack/plugins/index_management/public/application/sections/home/home.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx rename to x-pack/plugins/index_management/public/application/sections/home/home.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts b/x-pack/plugins/index_management/public/application/sections/home/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts b/x-pack/plugins/index_management/public/application/sections/home/index_list/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx index ced8bd97e744b9..9c31b0d650449d 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx @@ -32,7 +32,7 @@ import { } from '../../../../../../common/constants'; import { Template } from '../../../../../../common/types'; import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components'; -import { loadIndexTemplate } from '../../../../services/api'; +import { useLoadIndexTemplate } from '../../../../services/api'; import { decodePath } from '../../../../services/routing'; import { SendRequestResponse } from '../../../../../shared_imports'; import { useServices } from '../../../../app_context'; @@ -103,7 +103,7 @@ export const TemplateDetails: React.FunctionComponent = ({ }) => { const { uiMetricService } = useServices(); const decodedTemplateName = decodePath(templateName); - const { error, data: templateDetails, isLoading } = loadIndexTemplate(decodedTemplateName); + const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(decodedTemplateName); // TS complains if we use destructuring here. Fixed in 3.6.0 (https://github.com/microsoft/TypeScript/pull/31711). const isManaged = templateDetails ? templateDetails.isManaged : undefined; const [templateToDelete, setTemplateToDelete] = useState>([]); diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx similarity index 97% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx index 71c32e2e0177ff..ffdb224f162713 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { SectionError, SectionLoading, Error } from '../../../components'; import { TemplateTable } from './template_table'; -import { loadIndexTemplates } from '../../../services/api'; +import { useLoadIndexTemplates } from '../../../services/api'; import { Template } from '../../../../../common/types'; import { useServices } from '../../../app_context'; import { @@ -40,7 +40,7 @@ export const TemplateList: React.FunctionComponent { const { uiMetricService } = useServices(); - const { error, isLoading, data: templates, sendRequest: reload } = loadIndexTemplates(); + const { error, isLoading, data: templates, sendRequest: reload } = useLoadIndexTemplates(); let content; @@ -68,7 +68,7 @@ export const TemplateList: React.FunctionComponent { uiMetricService.trackMetric('loaded', UIM_TEMPLATE_LIST_LOAD); - }, []); + }, [uiMetricService]); if (isLoading) { content = ( diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts b/x-pack/plugins/index_management/public/application/sections/template_clone/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts rename to x-pack/plugins/index_management/public/application/sections/template_clone/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx rename to x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx index 6659be5a2cf4e3..cf6ca3c0657773 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx +++ b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx @@ -11,7 +11,7 @@ import { TemplateForm, SectionLoading, SectionError, Error } from '../../compone import { breadcrumbService } from '../../services/breadcrumbs'; import { decodePath, getTemplateDetailsLink } from '../../services/routing'; import { Template } from '../../../../common/types'; -import { saveTemplate, loadIndexTemplate } from '../../services/api'; +import { saveTemplate, useLoadIndexTemplate } from '../../services/api'; interface MatchParams { name: string; @@ -27,7 +27,7 @@ export const TemplateClone: React.FunctionComponent(false); const [saveError, setSaveError] = useState(null); - const { error: templateToCloneError, data: templateToClone, isLoading } = loadIndexTemplate( + const { error: templateToCloneError, data: templateToClone, isLoading } = useLoadIndexTemplate( decodedTemplateName ); diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts b/x-pack/plugins/index_management/public/application/sections/template_create/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts rename to x-pack/plugins/index_management/public/application/sections/template_create/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx b/x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx rename to x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts b/x-pack/plugins/index_management/public/application/sections/template_edit/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts rename to x-pack/plugins/index_management/public/application/sections/template_edit/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx rename to x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx index 69e446528a68d5..1e9d5f294de34c 100644 --- a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx +++ b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx @@ -8,7 +8,7 @@ import { RouteComponentProps } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPageBody, EuiPageContent, EuiTitle, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { breadcrumbService } from '../../services/breadcrumbs'; -import { loadIndexTemplate, updateTemplate } from '../../services/api'; +import { useLoadIndexTemplate, updateTemplate } from '../../services/api'; import { decodePath, getTemplateDetailsLink } from '../../services/routing'; import { SectionLoading, SectionError, TemplateForm, Error } from '../../components'; import { Template } from '../../../../common/types'; @@ -27,7 +27,7 @@ export const TemplateEdit: React.FunctionComponent(false); const [saveError, setSaveError] = useState(null); - const { error, data: template, isLoading } = loadIndexTemplate(decodedTemplateName); + const { error, data: template, isLoading } = useLoadIndexTemplate(decodedTemplateName); useEffect(() => { breadcrumbService.setBreadcrumbs('templateEdit'); diff --git a/x-pack/legacy/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/services/api.ts rename to x-pack/plugins/index_management/public/application/services/api.ts index ff85f992850565..4034897ebdd6c5 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/api.ts +++ b/x-pack/plugins/index_management/public/application/services/api.ts @@ -201,7 +201,7 @@ export async function loadIndexData(type: string, indexName: string) { } } -export function loadIndexTemplates() { +export function useLoadIndexTemplates() { return useRequest({ path: `${API_BASE_PATH}/templates`, method: 'get', @@ -221,7 +221,7 @@ export async function deleteTemplates(names: Array) { return result; } -export function loadIndexTemplate(name: Template['name']) { +export function useLoadIndexTemplate(name: Template['name']) { return useRequest({ path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`, method: 'get', diff --git a/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts b/x-pack/plugins/index_management/public/application/services/breadcrumbs.ts similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts rename to x-pack/plugins/index_management/public/application/services/breadcrumbs.ts index 299491756560ee..8128ccd545dceb 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts +++ b/x-pack/plugins/index_management/public/application/services/breadcrumbs.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; import { BASE_PATH } from '../../../common/constants'; -import { ManagementAppMountParams } from '../../../../../../../src/plugins/management/public'; +import { ManagementAppMountParams } from '../../../../../../src/plugins/management/public'; type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs']; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts b/x-pack/plugins/index_management/public/application/services/documentation.ts similarity index 98% rename from x-pack/legacy/plugins/index_management/public/application/services/documentation.ts rename to x-pack/plugins/index_management/public/application/services/documentation.ts index e0f261e586b1e7..fdf07c43a0c8b2 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts +++ b/x-pack/plugins/index_management/public/application/services/documentation.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DocLinksStart } from '../../../../../../../src/core/public'; +import { DocLinksStart } from '../../../../../../src/core/public'; import { DataType } from '../components/mappings_editor/types'; import { TYPE_DEFINITION } from '../components/mappings_editor/constants'; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts b/x-pack/plugins/index_management/public/application/services/health_to_color.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts rename to x-pack/plugins/index_management/public/application/services/health_to_color.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/http.ts b/x-pack/plugins/index_management/public/application/services/http.ts similarity index 88% rename from x-pack/legacy/plugins/index_management/public/application/services/http.ts rename to x-pack/plugins/index_management/public/application/services/http.ts index a6973c263f00f2..931e5fcd21898f 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/http.ts +++ b/x-pack/plugins/index_management/public/application/services/http.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from '../../../../../../../src/core/public'; +import { HttpSetup } from '../../../../../../src/core/public'; export class HttpService { private client: any; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/index.ts b/x-pack/plugins/index_management/public/application/services/index.ts similarity index 96% rename from x-pack/legacy/plugins/index_management/public/application/services/index.ts rename to x-pack/plugins/index_management/public/application/services/index.ts index 78ff8cb5c314ac..2334d32adf1314 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/index.ts +++ b/x-pack/plugins/index_management/public/application/services/index.ts @@ -21,7 +21,7 @@ export { loadIndexStats, loadIndexMapping, loadIndexData, - loadIndexTemplates, + useLoadIndexTemplates, } from './api'; export { healthToColor } from './health_to_color'; export { sortTable } from './sort_table'; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/navigation.ts b/x-pack/plugins/index_management/public/application/services/navigation.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/navigation.ts rename to x-pack/plugins/index_management/public/application/services/navigation.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/notification.ts b/x-pack/plugins/index_management/public/application/services/notification.ts similarity index 93% rename from x-pack/legacy/plugins/index_management/public/application/services/notification.ts rename to x-pack/plugins/index_management/public/application/services/notification.ts index 0971ca77c004bb..82b9de22727471 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/notification.ts +++ b/x-pack/plugins/index_management/public/application/services/notification.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { NotificationsStart } from '../../../../../../../src/core/public'; +import { NotificationsStart } from '../../../../../../src/core/public'; export class NotificationService { private _toasts: any; diff --git a/x-pack/legacy/plugins/index_management/public/application/services/routing.ts b/x-pack/plugins/index_management/public/application/services/routing.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/routing.ts rename to x-pack/plugins/index_management/public/application/services/routing.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts b/x-pack/plugins/index_management/public/application/services/sort_table.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts rename to x-pack/plugins/index_management/public/application/services/sort_table.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts b/x-pack/plugins/index_management/public/application/services/ui_metric.ts similarity index 90% rename from x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts rename to x-pack/plugins/index_management/public/application/services/ui_metric.ts index 7c87ec9509085c..73d2ef5aced861 100644 --- a/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts +++ b/x-pack/plugins/index_management/public/application/services/ui_metric.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { UiStatsMetricType } from '@kbn/analytics'; -import { UsageCollectionSetup } from '../../../../../../../src/plugins/usage_collection/public'; + +import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/public'; import { IndexMgmtMetricsType } from '../../types'; let uiMetricService: UiMetricService; @@ -23,7 +24,8 @@ export class UiMetricService { private track(type: T, name: string) { if (!this.usageCollection) { - throw Error('UiMetricService not initialized.'); + // Usage collection might have been disabled in Kibana config. + return; } this.usageCollection.reportUiStats(this.appName, type as UiStatsMetricType, name); } diff --git a/x-pack/legacy/plugins/index_management/public/application/services/use_request.ts b/x-pack/plugins/index_management/public/application/services/use_request.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/services/use_request.ts rename to x-pack/plugins/index_management/public/application/services/use_request.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js b/x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js b/x-pack/plugins/index_management/public/application/store/actions/clear_row_status.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js rename to x-pack/plugins/index_management/public/application/store/actions/clear_row_status.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js b/x-pack/plugins/index_management/public/application/store/actions/close_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/close_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js b/x-pack/plugins/index_management/public/application/store/actions/delete_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/delete_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js b/x-pack/plugins/index_management/public/application/store/actions/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js rename to x-pack/plugins/index_management/public/application/store/actions/detail_panel.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js b/x-pack/plugins/index_management/public/application/store/actions/edit_index_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js rename to x-pack/plugins/index_management/public/application/store/actions/edit_index_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js b/x-pack/plugins/index_management/public/application/store/actions/extension_action.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js rename to x-pack/plugins/index_management/public/application/store/actions/extension_action.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js b/x-pack/plugins/index_management/public/application/store/actions/flush_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/flush_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js b/x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js b/x-pack/plugins/index_management/public/application/store/actions/freeze_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/freeze_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/index.js b/x-pack/plugins/index_management/public/application/store/actions/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/index.js rename to x-pack/plugins/index_management/public/application/store/actions/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js b/x-pack/plugins/index_management/public/application/store/actions/load_index_data.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js rename to x-pack/plugins/index_management/public/application/store/actions/load_index_data.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js b/x-pack/plugins/index_management/public/application/store/actions/load_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/load_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js b/x-pack/plugins/index_management/public/application/store/actions/open_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/open_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js b/x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js b/x-pack/plugins/index_management/public/application/store/actions/reload_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/reload_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js b/x-pack/plugins/index_management/public/application/store/actions/table_state.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js rename to x-pack/plugins/index_management/public/application/store/actions/table_state.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js b/x-pack/plugins/index_management/public/application/store/actions/unfreeze_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js rename to x-pack/plugins/index_management/public/application/store/actions/unfreeze_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js b/x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js rename to x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/index.ts b/x-pack/plugins/index_management/public/application/store/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/index.ts rename to x-pack/plugins/index_management/public/application/store/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js b/x-pack/plugins/index_management/public/application/store/reducers/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js rename to x-pack/plugins/index_management/public/application/store/reducers/detail_panel.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js b/x-pack/plugins/index_management/public/application/store/reducers/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js rename to x-pack/plugins/index_management/public/application/store/reducers/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js b/x-pack/plugins/index_management/public/application/store/reducers/index_management.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js rename to x-pack/plugins/index_management/public/application/store/reducers/index_management.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js b/x-pack/plugins/index_management/public/application/store/reducers/indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js rename to x-pack/plugins/index_management/public/application/store/reducers/indices.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js b/x-pack/plugins/index_management/public/application/store/reducers/row_status.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js rename to x-pack/plugins/index_management/public/application/store/reducers/row_status.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js b/x-pack/plugins/index_management/public/application/store/reducers/table_state.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js rename to x-pack/plugins/index_management/public/application/store/reducers/table_state.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts b/x-pack/plugins/index_management/public/application/store/selectors/index.d.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts rename to x-pack/plugins/index_management/public/application/store/selectors/index.d.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js rename to x-pack/plugins/index_management/public/application/store/selectors/index.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/store.d.ts b/x-pack/plugins/index_management/public/application/store/store.d.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/store.d.ts rename to x-pack/plugins/index_management/public/application/store/store.d.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/store.js b/x-pack/plugins/index_management/public/application/store/store.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/application/store/store.js rename to x-pack/plugins/index_management/public/application/store/store.js diff --git a/x-pack/legacy/plugins/index_management/public/index.scss b/x-pack/plugins/index_management/public/index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/index.scss rename to x-pack/plugins/index_management/public/index.scss diff --git a/x-pack/legacy/plugins/index_management/public/index.ts b/x-pack/plugins/index_management/public/index.ts similarity index 66% rename from x-pack/legacy/plugins/index_management/public/index.ts rename to x-pack/plugins/index_management/public/index.ts index 16e7bf21aee98d..6bb921ef648f34 100644 --- a/x-pack/legacy/plugins/index_management/public/index.ts +++ b/x-pack/plugins/index_management/public/index.ts @@ -3,19 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { npSetup } from 'ui/new_platform'; - +import './index.scss'; import { IndexMgmtUIPlugin, IndexMgmtSetup } from './plugin'; /** @public */ -export { IndexMgmtSetup }; - export const plugin = () => { return new IndexMgmtUIPlugin(); }; -// Temp. To be removed after moving to the "plugins" folder - -const { extensionsService } = plugin().setup(npSetup.core, npSetup.plugins); +export { IndexMgmtSetup }; -export { extensionsService }; +export { getIndexListUri } from './application/services/navigation'; diff --git a/x-pack/legacy/plugins/index_management/public/mocks.ts b/x-pack/plugins/index_management/public/mocks.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/mocks.ts rename to x-pack/plugins/index_management/public/mocks.ts diff --git a/x-pack/legacy/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts similarity index 90% rename from x-pack/legacy/plugins/index_management/public/plugin.ts rename to x-pack/plugins/index_management/public/plugin.ts index 539324766cf955..c1b26fe3ca56b4 100644 --- a/x-pack/legacy/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { CoreSetup } from '../../../../../src/core/public'; -import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public'; -import { ManagementSetup } from '../../../../../src/plugins/management/public'; -import { UIM_APP_NAME } from '../common/constants'; +import { CoreSetup } from '../../../../src/core/public'; +import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; +import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { UIM_APP_NAME, PLUGIN } from '../common/constants'; import { AppDependencies } from './application'; import { httpService } from './application/services/http'; @@ -52,7 +52,7 @@ export class IndexMgmtUIPlugin { this.uiMetricService.setup(usageCollection); management.sections.getSection('elasticsearch')!.registerApp({ - id: 'index_management', + id: PLUGIN.id, title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), order: 1, mount: async ({ element, setBreadcrumbs }) => { diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts b/x-pack/plugins/index_management/public/services/extensions_service.mock.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts rename to x-pack/plugins/index_management/public/services/extensions_service.mock.ts diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.ts b/x-pack/plugins/index_management/public/services/extensions_service.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/services/extensions_service.ts rename to x-pack/plugins/index_management/public/services/extensions_service.ts diff --git a/x-pack/legacy/plugins/index_management/public/services/index.ts b/x-pack/plugins/index_management/public/services/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/services/index.ts rename to x-pack/plugins/index_management/public/services/index.ts diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts new file mode 100644 index 00000000000000..cd2964df23d9bf --- /dev/null +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { + SendRequestConfig, + SendRequestResponse, + UseRequestConfig, + sendRequest, + useRequest, +} from '../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; + +export { + FormSchema, + FIELD_TYPES, + VALIDATION_TYPES, + FieldConfig, + useForm, + Form, + getUseField, +} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; + +export { + fieldFormatters, + fieldValidators, + serializers, +} from '../../../../src/plugins/es_ui_shared/static/forms/helpers'; + +export { getFormRow, Field } from '../../../../src/plugins/es_ui_shared/static/forms/components'; + +export { isJSON } from '../../../../src/plugins/es_ui_shared/static/validators/string'; diff --git a/x-pack/legacy/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/types.ts rename to x-pack/plugins/index_management/public/types.ts diff --git a/x-pack/plugins/index_management/server/config.ts b/x-pack/plugins/index_management/server/config.ts new file mode 100644 index 00000000000000..5f03575d3ff436 --- /dev/null +++ b/x-pack/plugins/index_management/server/config.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), +}); + +export type IndexManagementConfig = TypeOf; diff --git a/x-pack/legacy/plugins/index_management/server/index.ts b/x-pack/plugins/index_management/server/index.ts similarity index 67% rename from x-pack/legacy/plugins/index_management/server/index.ts rename to x-pack/plugins/index_management/server/index.ts index 866b374740d3b2..e4102711708cbb 100644 --- a/x-pack/legacy/plugins/index_management/server/index.ts +++ b/x-pack/plugins/index_management/server/index.ts @@ -5,8 +5,18 @@ */ import { PluginInitializerContext } from 'src/core/server'; + import { IndexMgmtServerPlugin } from './plugin'; +import { configSchema } from './config'; export const plugin = (ctx: PluginInitializerContext) => new IndexMgmtServerPlugin(ctx); +export const config = { + schema: configSchema, +}; + +/** @public */ export { Dependencies } from './types'; +export { IndexMgmtSetup } from './plugin'; +export { Index } from './types'; +export { IndexManagementConfig } from './config'; diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.test.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.test.ts rename to x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.ts rename to x-pack/plugins/index_management/server/lib/fetch_aliases.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/fetch_indices.ts rename to x-pack/plugins/index_management/server/lib/fetch_indices.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/get_managed_templates.ts b/x-pack/plugins/index_management/server/lib/get_managed_templates.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/get_managed_templates.ts rename to x-pack/plugins/index_management/server/lib/get_managed_templates.ts diff --git a/x-pack/legacy/plugins/index_management/server/lib/is_es_error.ts b/x-pack/plugins/index_management/server/lib/is_es_error.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/server/lib/is_es_error.ts rename to x-pack/plugins/index_management/server/lib/is_es_error.ts diff --git a/x-pack/legacy/plugins/index_management/server/plugin.ts b/x-pack/plugins/index_management/server/plugin.ts similarity index 94% rename from x-pack/legacy/plugins/index_management/server/plugin.ts rename to x-pack/plugins/index_management/server/plugin.ts index 95d27e1cf16bae..a0a9151cdb71f5 100644 --- a/x-pack/legacy/plugins/index_management/server/plugin.ts +++ b/x-pack/plugins/index_management/server/plugin.ts @@ -24,8 +24,8 @@ export class IndexMgmtServerPlugin implements Plugin { - const href = `/${hrefBase}/settings`; + const href = '/settings'; return ( { {uiCapabilities?.infrastructure?.configureSource ? ( - + {i18n.translate('xpack.infra.configureSourceActionLabel', { defaultMessage: 'Change source configuration', })} diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx index 1294007240027b..739bad5689a96f 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx @@ -10,10 +10,7 @@ import { identity } from 'fp-ts/lib/function'; import React from 'react'; import { NoIndices } from '../../../components/empty_states/no_indices'; -import { - ViewSourceConfigurationButton, - ViewSourceConfigurationButtonHrefBase, -} from '../../../components/source_configuration'; +import { ViewSourceConfigurationButton } from '../../../components/source_configuration'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; export const LogsPageNoIndicesContent = () => { @@ -49,10 +46,7 @@ export const LogsPageNoIndicesContent = () => { {canConfigureSource ? ( - + {i18n.translate('xpack.infra.configureSourceActionLabel', { defaultMessage: 'Change source configuration', })} diff --git a/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx index fde3b61de50b58..43f684cd5a5853 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx @@ -10,10 +10,7 @@ import { identity } from 'fp-ts/lib/function'; import React from 'react'; import { euiStyled } from '../../../../../observability/public'; -import { - ViewSourceConfigurationButton, - ViewSourceConfigurationButtonHrefBase, -} from '../../../components/source_configuration'; +import { ViewSourceConfigurationButton } from '../../../components/source_configuration'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; interface InvalidNodeErrorProps { @@ -59,10 +56,7 @@ export const InvalidNodeError: React.FunctionComponent = - + { - test(`route that doesn't start with "/api/" continues`, async () => { - const mockHTTPSetup = coreMock.createSetup().http; - initAPIAuthorization( - mockHTTPSetup, - authorizationMock.create(), - loggingServiceMock.create().get() - ); - - const [[postAuthHandler]] = mockHTTPSetup.registerOnPostAuth.mock.calls; - - const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', path: '/app/foo' }); - const mockResponse = httpServerMock.createResponseFactory(); - const mockPostAuthToolkit = httpServiceMock.createOnPostAuthToolkit(); - - await postAuthHandler(mockRequest, mockResponse, mockPostAuthToolkit); - - expect(mockResponse.notFound).not.toHaveBeenCalled(); - expect(mockPostAuthToolkit.next).toHaveBeenCalledTimes(1); - }); - - test(`protected route that starts with "/api/", but "mode.useRbacForRequest()" returns false continues`, async () => { + test(`protected route when "mode.useRbacForRequest()" returns false continues`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create(); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -44,7 +24,7 @@ describe('initAPIAuthorization', () => { const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', routeTags: ['access:foo'], }); const mockResponse = httpServerMock.createResponseFactory(); @@ -59,7 +39,7 @@ describe('initAPIAuthorization', () => { expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); - test(`unprotected route that starts with "/api/", but "mode.useRbacForRequest()" returns true continues`, async () => { + test(`unprotected route when "mode.useRbacForRequest()" returns true continues`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create(); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -68,7 +48,7 @@ describe('initAPIAuthorization', () => { const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', routeTags: ['not-access:foo'], }); const mockResponse = httpServerMock.createResponseFactory(); @@ -83,7 +63,7 @@ describe('initAPIAuthorization', () => { expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); - test(`protected route that starts with "/api/", "mode.useRbacForRequest()" returns true and user is authorized continues`, async () => { + test(`protected route when "mode.useRbacForRequest()" returns true and user is authorized continues`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create({ version: '1.0.0-zeta1' }); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -93,7 +73,7 @@ describe('initAPIAuthorization', () => { const headers = { authorization: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', headers, routeTags: ['access:foo'], }); @@ -118,7 +98,7 @@ describe('initAPIAuthorization', () => { expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); - test(`protected route that starts with "/api/", "mode.useRbacForRequest()" returns true and user isn't authorized responds with a 404`, async () => { + test(`protected route when "mode.useRbacForRequest()" returns true and user isn't authorized responds with a 404`, async () => { const mockHTTPSetup = coreMock.createSetup().http; const mockAuthz = authorizationMock.create({ version: '1.0.0-zeta1' }); initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get()); @@ -128,7 +108,7 @@ describe('initAPIAuthorization', () => { const headers = { authorization: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: '/api/foo', + path: '/foo/bar', headers, routeTags: ['access:foo'], }); diff --git a/x-pack/plugins/security/server/authorization/api_authorization.ts b/x-pack/plugins/security/server/authorization/api_authorization.ts index b280cc74c230f5..cc672fbc69e068 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.ts @@ -13,8 +13,8 @@ export function initAPIAuthorization( logger: Logger ) { http.registerOnPostAuth(async (request, response, toolkit) => { - // if the api doesn't start with "/api/" or we aren't using RBAC for this request, just continue - if (!request.url.path!.startsWith('/api/') || !mode.useRbacForRequest(request)) { + // if we aren't using RBAC for this request, just continue + if (!mode.useRbacForRequest(request)) { return toolkit.next(); } diff --git a/x-pack/plugins/spaces/common/index.ts b/x-pack/plugins/spaces/common/index.ts index c1f0f8bd3ece41..703722fcf8f52c 100644 --- a/x-pack/plugins/spaces/common/index.ts +++ b/x-pack/plugins/spaces/common/index.ts @@ -5,5 +5,5 @@ */ export { isReservedSpace } from './is_reserved_space'; -export { MAX_SPACE_INITIALS, SPACE_SEARCH_COUNT_THRESHOLD } from './constants'; +export { MAX_SPACE_INITIALS, SPACE_SEARCH_COUNT_THRESHOLD, ENTER_SPACE_PATH } from './constants'; export { addSpaceIdToPath, getSpaceIdFromPath } from './lib/spaces_url_parser'; 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 59656333f865ef..cdfbc9bbe2683b 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 @@ -14,7 +14,7 @@ import { import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, ReactElement } from 'react'; import { Capabilities, ApplicationStart } from 'src/core/public'; -import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common/constants'; +import { addSpaceIdToPath, SPACE_SEARCH_COUNT_THRESHOLD, ENTER_SPACE_PATH } from '../../../common'; import { Space } from '../../../common/model/space'; import { ManageSpacesButton } from './manage_spaces_button'; import { SpaceAvatar } from '../../space_avatar'; @@ -23,7 +23,7 @@ interface Props { id: string; spaces: Space[]; isLoading: boolean; - onSelectSpace: (space: Space) => void; + serverBasePath: string; onManageSpacesClick: () => void; intl: InjectedIntl; capabilities: Capabilities; @@ -184,7 +184,7 @@ class SpacesMenuUI extends Component { diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx index 53d7038cec4d57..4d7af256822c83 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx @@ -23,6 +23,7 @@ export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreSta { const wrapper = shallow( { const wrapper = mountWithIntl( { id={popoutContentId} spaces={this.state.spaces} isLoading={this.state.loading} - onSelectSpace={this.onSelectSpace} + serverBasePath={this.props.serverBasePath} onManageSpacesClick={this.toggleSpaceSelector} capabilities={this.props.capabilities} navigateToApp={this.props.navigateToApp} @@ -175,8 +176,4 @@ export class NavControlPopover extends Component { showSpaceSelector: false, }); }; - - private onSelectSpace = (space: Space) => { - this.props.spacesManager.changeSelectedSpace(space); - }; } diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index 73dae84c1873bd..44215ec5380025 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -43,8 +43,7 @@ export class SpacesPlugin implements Plugin { const space = { @@ -16,21 +17,33 @@ test('it renders without crashing', () => { disabledFeatures: [], }; - shallow(); + shallow(); }); -test('it is clickable', () => { +test('links to the indicated space', () => { const space = { - id: '', + id: 'some-space', name: 'space name', description: 'space description', disabledFeatures: [], }; - const clickHandler = jest.fn(); + const wrapper = mount(); + expect(wrapper.find(EuiCard).props()).toMatchObject({ + href: '/server-base-path/s/some-space/spaces/enter', + }); +}); - const wrapper = mount(); - wrapper.find('button').simulate('click'); +test('links to the default space too', () => { + const space = { + id: 'default', + name: 'default space', + description: 'space description', + disabledFeatures: [], + }; - expect(clickHandler).toHaveBeenCalledTimes(1); + const wrapper = mount(); + expect(wrapper.find(EuiCard).props()).toMatchObject({ + href: '/server-base-path/spaces/enter', + }); }); 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 f898ba87c60bd6..7a056427fb166e 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 @@ -6,15 +6,16 @@ import { EuiCard } from '@elastic/eui'; import React from 'react'; -import { Space } from '../../../common/model/space'; +import { addSpaceIdToPath, ENTER_SPACE_PATH } from '../../../common'; import { SpaceAvatar } from '../../space_avatar'; +import { Space } from '../..'; interface Props { space: Space; - onClick: () => void; + serverBasePath: string; } export const SpaceCard = (props: Props) => { - const { space, onClick } = props; + const { serverBasePath, space } = props; return ( { icon={renderSpaceAvatar(space)} title={space.name} description={renderSpaceDescription(space)} - onClick={onClick} + href={addSpaceIdToPath(serverBasePath, space.id, ENTER_SPACE_PATH)} /> ); }; diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx index 07b85114e3c8f2..8de22bcbe235f9 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx @@ -16,5 +16,5 @@ test('it renders without crashing', () => { disabledFeatures: [], }; - shallow(); + shallow(); }); 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 7c4084d36b21dc..b480cf524304f5 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 @@ -11,7 +11,7 @@ import { SpaceCard } from './space_card'; interface Props { spaces: Space[]; - onSpaceSelect: (space: Space) => void; + serverBasePath: string; } export class SpaceCards extends Component { @@ -25,15 +25,9 @@ export class SpaceCards extends Component { ); } - public renderSpace = (space: Space) => ( + private renderSpace = (space: Space) => ( - + ); - - public createSpaceClickHandler = (space: Space) => { - return () => { - this.props.onSpaceSelect(space); - }; - }; } diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx index c8173de1661be7..112a8a9a0a6857 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx @@ -18,7 +18,9 @@ function getSpacesManager(spaces: Space[] = []) { test('it renders without crashing', () => { const spacesManager = getSpacesManager(); - const component = shallowWithIntl(); + const component = shallowWithIntl( + + ); expect(component).toMatchSnapshot(); }); @@ -34,7 +36,9 @@ test('it queries for spaces when loaded', () => { const spacesManager = getSpacesManager(spaces); - shallowWithIntl(); + shallowWithIntl( + + ); return Promise.resolve().then(() => { expect(spacesManager.getSpaces).toHaveBeenCalledTimes(1); 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 b63de399b95c99..9289784b9f95f0 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx @@ -30,6 +30,7 @@ import { SpacesManager } from '../spaces_manager'; interface Props { spacesManager: SpacesManager; + serverBasePath: string; } interface State { @@ -129,7 +130,7 @@ export class SpaceSelector extends Component { {this.state.loading && } {!this.state.loading && ( - + )} {!this.state.loading && filteredSpaces.length === 0 && ( @@ -179,10 +180,6 @@ export class SpaceSelector extends Component { searchTerm: searchTerm.trim().toLowerCase(), }); }; - - public onSelectSpace = (space: Space) => { - this.props.spacesManager.changeSelectedSpace(space); - }; } export const renderSpaceSelectorApp = (i18nStart: CoreStart['i18n'], el: Element, props: Props) => { diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx index 29ead8d34994de..6fab1767e4b6d3 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx @@ -29,7 +29,10 @@ export const spaceSelectorApp = Object.freeze({ getStartServices(), import('./space_selector'), ]); - return renderSpaceSelectorApp(coreStart.i18n, params.element, { spacesManager }); + return renderSpaceSelectorApp(coreStart.i18n, params.element, { + spacesManager, + serverBasePath: coreStart.http.basePath.serverBasePath, + }); }, }); }, diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts index 56879af33916fc..6186ac7fd93be6 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts @@ -20,7 +20,6 @@ function createSpacesManagerMock() { copySavedObjects: jest.fn().mockResolvedValue(undefined), resolveCopySavedObjectsErrors: jest.fn().mockResolvedValue(undefined), redirectToSpaceSelector: jest.fn().mockResolvedValue(undefined), - changeSelectedSpace: jest.fn(), } as unknown) as jest.Mocked; } diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts index f89bfc32a69e63..508669361c23ff 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts @@ -12,14 +12,14 @@ describe('SpacesManager', () => { describe('#constructor', () => { it('attempts to retrieve the active space', () => { const coreStart = coreMock.createStart(); - new SpacesManager('/server-base-path', coreStart.http); + new SpacesManager(coreStart.http); expect(coreStart.http.get).toHaveBeenCalledWith('/internal/spaces/_active_space'); }); it('does not retrieve the active space if on an anonymous path', () => { const coreStart = coreMock.createStart(); coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(true); - new SpacesManager('/server-base-path', coreStart.http); + new SpacesManager(coreStart.http); expect(coreStart.http.get).not.toHaveBeenCalled(); }); }); @@ -31,7 +31,7 @@ describe('SpacesManager', () => { id: 'my-space', name: 'my space', }); - const spacesManager = new SpacesManager('/server-base-path', coreStart.http); + const spacesManager = new SpacesManager(coreStart.http); expect(coreStart.http.get).toHaveBeenCalledWith('/internal/spaces/_active_space'); await nextTick(); @@ -47,7 +47,7 @@ describe('SpacesManager', () => { it('throws if on an anonymous path', () => { const coreStart = coreMock.createStart(); coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(true); - const spacesManager = new SpacesManager('/server-base-path', coreStart.http); + const spacesManager = new SpacesManager(coreStart.http); expect(coreStart.http.get).not.toHaveBeenCalled(); expect(() => spacesManager.getActiveSpace()).toThrowErrorMatchingInlineSnapshot( @@ -67,7 +67,7 @@ describe('SpacesManager', () => { name: 'my other space', }); - const spacesManager = new SpacesManager('/server-base-path', coreStart.http); + const spacesManager = new SpacesManager(coreStart.http); expect(coreStart.http.get).toHaveBeenCalledWith('/internal/spaces/_active_space'); await nextTick(); @@ -95,7 +95,7 @@ describe('SpacesManager', () => { name: 'my space', }); - const spacesManager = new SpacesManager('/server-base-path', coreStart.http); + const spacesManager = new SpacesManager(coreStart.http); expect(() => spacesManager.getActiveSpace({ forceRefresh: true }) diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts index e151dcd4f9368d..cc3f51b1850def 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts @@ -9,16 +9,18 @@ import { HttpSetup } from 'src/core/public'; import { SavedObjectsManagementRecord } from 'src/legacy/core_plugins/management/public'; import { Space } from '../../common/model/space'; import { GetSpacePurpose } from '../../common/model/types'; -import { ENTER_SPACE_PATH } from '../../common/constants'; import { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; -import { addSpaceIdToPath } from '../../common'; export class SpacesManager { private activeSpace$: BehaviorSubject = new BehaviorSubject(null); + private readonly serverBasePath: string; + public readonly onActiveSpaceChange$: Observable; - constructor(private readonly serverBasePath: string, private readonly http: HttpSetup) { + constructor(private readonly http: HttpSetup) { + this.serverBasePath = http.basePath.serverBasePath; + this.onActiveSpaceChange$ = this.activeSpace$ .asObservable() .pipe(skipWhile((v: Space | null) => v == null)) as Observable; @@ -99,10 +101,6 @@ export class SpacesManager { }); } - public async changeSelectedSpace(space: Space) { - window.location.href = addSpaceIdToPath(this.serverBasePath, space.id, ENTER_SPACE_PATH); - } - public redirectToSpaceSelector() { window.location.href = `${this.serverBasePath}/spaces/space_selector`; } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 33f69f8bbbc21c..5ec28be7ffceff 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2872,7 +2872,6 @@ "visualizations.newVisWizard.title": "新規ビジュアライゼーション", "visualizations.newVisWizard.visTypeAliasDescription": "Visualize外でKibanaアプリケーションを開きます。", "visualizations.newVisWizard.visTypeAliasTitle": "Kibanaアプリケーション", - "visualizations.queryGeohashBounds.unableToGetBoundErrorTitle": "バウンドを取得できませんでした", "visDefaultEditor.aggSelect.aggregationLabel": "集約", "visDefaultEditor.aggSelect.helpLinkLabel": "{aggTitle} のヘルプ", "visDefaultEditor.aggSelect.noCompatibleAggsDescription": "インデックスパターン{indexPatternTitle}には集約可能なフィールドが含まれていません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 10eddca3637f00..208f79129aad27 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2873,7 +2873,6 @@ "visualizations.newVisWizard.title": "新建可视化", "visualizations.newVisWizard.visTypeAliasDescription": "打开 Visualize 外部的 Kibana 应用程序。", "visualizations.newVisWizard.visTypeAliasTitle": "Kibana 应用程序", - "visualizations.queryGeohashBounds.unableToGetBoundErrorTitle": "无法获取边界", "visDefaultEditor.aggSelect.aggregationLabel": "聚合", "visDefaultEditor.aggSelect.helpLinkLabel": "{aggTitle} 帮助", "visDefaultEditor.aggSelect.noCompatibleAggsDescription": "索引模式“{indexPatternTitle}”不包含任何聚合。", diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts index 12e08869fa5861..959a0c97acfa3d 100644 --- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts +++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { AgentConfigurationIntake } from '../../../../plugins/apm/server/lib/settings/agent_configuration/configuration_types'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function agentConfigurationTests({ getService }: FtrProviderContext) { @@ -18,108 +19,122 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo'); } - let createdConfigIds: any[] = []; - async function createConfiguration(configuration: any) { + async function createConfiguration(config: AgentConfigurationIntake) { + log.debug('creating configuration', config.service); const res = await supertest - .post(`/api/apm/settings/agent-configuration/new`) - .send(configuration) + .put(`/api/apm/settings/agent-configuration`) + .send(config) .set('kbn-xsrf', 'foo'); - createdConfigIds.push(res.body._id); + throwOnError(res); return res; } - function deleteCreatedConfigurations() { - const promises = Promise.all(createdConfigIds.map(deleteConfiguration)); - return promises; + async function updateConfiguration(config: AgentConfigurationIntake) { + log.debug('updating configuration', config.service); + const res = await supertest + .put(`/api/apm/settings/agent-configuration?overwrite=true`) + .send(config) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; } - function deleteConfiguration(configurationId: string) { - return supertest - .delete(`/api/apm/settings/agent-configuration/${configurationId}`) - .set('kbn-xsrf', 'foo') - .then((response: any) => { - createdConfigIds = createdConfigIds.filter(id => id === configurationId); - return response; - }); + async function deleteConfiguration({ service }: AgentConfigurationIntake) { + log.debug('deleting configuration', service); + const res = await supertest + .delete(`/api/apm/settings/agent-configuration`) + .send({ service }) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + function throwOnError(res: any) { + const { statusCode, req, body } = res; + if (statusCode !== 200) { + throw new Error(` + Endpoint: ${req.method} ${req.path} + Service: ${JSON.stringify(res.request._data.service)} + Status code: ${statusCode} + Response: ${body.message}`); + } } describe('agent configuration', () => { describe('when creating one configuration', () => { - let createdConfigId: string; + const newConfig = { + service: {}, + settings: { transaction_sample_rate: 0.55 }, + }; - const parameters = { + const searchParams = { service: { name: 'myservice', environment: 'development' }, etag: '7312bdcc34999629a3d39df24ed9b2a7553c0c39', }; before(async () => { - log.debug('creating agent configuration'); - - // all / all - const { body } = await createConfiguration({ - service: {}, - settings: { transaction_sample_rate: 0.1 }, - }); - - createdConfigId = body._id; + await createConfiguration(newConfig); }); - it('returns the created configuration', async () => { - const { statusCode, body } = await searchConfigurations(parameters); - + it('can find the created config', async () => { + const { statusCode, body } = await searchConfigurations(searchParams); expect(statusCode).to.equal(200); - expect(body._id).to.equal(createdConfigId); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: 0.55 }); }); - it('succesfully deletes the configuration', async () => { - await deleteConfiguration(createdConfigId); + it('can update the created config', async () => { + await updateConfiguration({ service: {}, settings: { transaction_sample_rate: 0.85 } }); - const { statusCode } = await searchConfigurations(parameters); + const { statusCode, body } = await searchConfigurations(searchParams); + expect(statusCode).to.equal(200); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: 0.85 }); + }); + it('can delete the created config', async () => { + await deleteConfiguration(newConfig); + const { statusCode } = await searchConfigurations(searchParams); expect(statusCode).to.equal(404); }); }); - describe('when creating four configurations', () => { - before(async () => { - log.debug('creating agent configuration'); - - // all / all - await createConfiguration({ + describe('when creating multiple configurations', () => { + const configs = [ + { service: {}, settings: { transaction_sample_rate: 0.1 }, - }); - - // my_service / all - await createConfiguration({ + }, + { service: { name: 'my_service' }, settings: { transaction_sample_rate: 0.2 }, - }); - - // all / production - await createConfiguration({ - service: { environment: 'production' }, + }, + { + service: { name: 'my_service', environment: 'development' }, settings: { transaction_sample_rate: 0.3 }, - }); - - // all / production - await createConfiguration({ - service: { environment: 'development' }, + }, + { + service: { environment: 'production' }, settings: { transaction_sample_rate: 0.4 }, - }); - - // my_service / production - await createConfiguration({ - service: { name: 'my_service', environment: 'development' }, + }, + { + service: { environment: 'development' }, settings: { transaction_sample_rate: 0.5 }, - }); + }, + ]; + + before(async () => { + await Promise.all(configs.map(config => createConfiguration(config))); }); after(async () => { - log.debug('deleting agent configurations'); - await deleteCreatedConfigurations(); + await Promise.all(configs.map(config => deleteConfiguration(config))); }); const agentsRequests = [ @@ -127,20 +142,24 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte service: { name: 'non_existing_service', environment: 'non_existing_env' }, expectedSettings: { transaction_sample_rate: 0.1 }, }, + { + service: { name: 'my_service', environment: 'non_existing_env' }, + expectedSettings: { transaction_sample_rate: 0.2 }, + }, { service: { name: 'my_service', environment: 'production' }, expectedSettings: { transaction_sample_rate: 0.2 }, }, { - service: { name: 'non_existing_service', environment: 'production' }, + service: { name: 'my_service', environment: 'development' }, expectedSettings: { transaction_sample_rate: 0.3 }, }, { - service: { name: 'non_existing_service', environment: 'development' }, + service: { name: 'non_existing_service', environment: 'production' }, expectedSettings: { transaction_sample_rate: 0.4 }, }, { - service: { name: 'my_service', environment: 'development' }, + service: { name: 'non_existing_service', environment: 'development' }, expectedSettings: { transaction_sample_rate: 0.5 }, }, ]; @@ -159,18 +178,18 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); describe('when an agent retrieves a configuration', () => { + const config = { + service: { name: 'myservice', environment: 'development' }, + settings: { transaction_sample_rate: 0.9 }, + }; + before(async () => { log.debug('creating agent configuration'); - - await createConfiguration({ - service: { name: 'myservice', environment: 'development' }, - settings: { transaction_sample_rate: 0.9 }, - }); + await createConfiguration(config); }); after(async () => { - log.debug('deleting agent configurations'); - await deleteCreatedConfigurations(); + await deleteConfiguration(config); }); it(`should have 'applied_by_agent=false' on first request`, async () => { diff --git a/x-pack/test/api_integration/apis/apm/feature_controls.ts b/x-pack/test/api_integration/apis/apm/feature_controls.ts index ec2bbca23ddd28..3c5314d0d32613 100644 --- a/x-pack/test/api_integration/apis/apm/feature_controls.ts +++ b/x-pack/test/api_integration/apis/apm/feature_controls.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -/* eslint-disable no-console */ - import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -14,8 +12,8 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const supertestWithoutAuth = getService('supertestWithoutAuth'); const security = getService('security'); const spaces = getService('spaces'); - const log = getService('log'); const es = getService('legacyEs'); + const log = getService('log'); const start = encodeURIComponent(new Date(Date.now() - 10000).toISOString()); const end = encodeURIComponent(new Date().toISOString()); @@ -33,7 +31,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) interface Endpoint { req: { url: string; - method?: 'get' | 'post' | 'delete'; + method?: 'get' | 'post' | 'delete' | 'put'; body?: any; }; expectForbidden: (result: any) => void; @@ -148,7 +146,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) index: '.apm-agent-configuration', }); - console.warn(JSON.stringify(res, null, 2)); + log.error(JSON.stringify(res, null, 2)); }, }, ]; @@ -196,7 +194,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const { statusCode, req } = response; if (statusCode !== 200) { - log.debug(`Endpoint: ${req.method} ${req.path} + throw new Error(`Endpoint: ${req.method} ${req.path} Status code: ${statusCode} Response: ${response.body.message}`); } @@ -216,9 +214,9 @@ export default function featureControlsTests({ getService }: FtrProviderContext) spaceId?: string; }) { for (const endpoint of endpoints) { - console.log(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`); + log.info(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`); const result = await executeAsUser(endpoint.req, username, password, spaceId); - console.log(`Responded: ${endpoint.req.url}`); + log.info(`Responded: ${endpoint.req.url}`); try { if (expectation === 'forbidden') { @@ -244,26 +242,28 @@ export default function featureControlsTests({ getService }: FtrProviderContext) } describe('apm feature controls', () => { - let res: any; + const config = { + service: { name: 'test-service' }, + settings: { transaction_sample_rate: 0.5 }, + }; before(async () => { - console.log(`Creating agent configuration`); - res = await executeAsAdmin({ - method: 'post', - url: '/api/apm/settings/agent-configuration/new', - body: { - service: { name: 'test-service' }, - settings: { transaction_sample_rate: 0.5 }, - }, + log.info(`Creating agent configuration`); + await executeAsAdmin({ + method: 'put', + url: '/api/apm/settings/agent-configuration', + body: config, }); - console.log(`Agent configuration created`); + log.info(`Agent configuration created`); }); after(async () => { - console.log('deleting agent configuration'); - const configurationId = res.body._id; + log.info('deleting agent configuration'); await executeAsAdmin({ method: 'delete', - url: `/api/apm/settings/agent-configuration/${configurationId}`, + url: `/api/apm/settings/agent-configuration`, + body: { + service: config.service, + }, }); }); diff --git a/x-pack/test/api_integration/apis/apm/index.ts b/x-pack/test/api_integration/apis/apm/index.ts index c49d5775370485..6f41f4abfecc3a 100644 --- a/x-pack/test/api_integration/apis/apm/index.ts +++ b/x-pack/test/api_integration/apis/apm/index.ts @@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('APM', () => { + describe('APM specs', () => { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./agent_configuration')); }); diff --git a/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts b/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts index f89905f0da04fe..4d3167b14b86f2 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts +++ b/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts @@ -13,7 +13,8 @@ export const makePing = async ( es: any, monitorId: string, fields: { [key: string]: any }, - mogrify: (doc: any) => any + mogrify: (doc: any) => any, + refresh: boolean = true ) => { const baseDoc = { tcp: { @@ -103,7 +104,7 @@ export const makePing = async ( await es.index({ index: INDEX_NAME, - refresh: true, + refresh, body: doc, }); @@ -115,7 +116,8 @@ export const makeCheck = async ( monitorId: string, numIps: number, fields: { [key: string]: any }, - mogrify: (doc: any) => any + mogrify: (doc: any) => any, + refresh: boolean = true ) => { const cgFields = { monitor: { @@ -137,11 +139,16 @@ export const makeCheck = async ( if (i === numIps - 1) { pingFields.summary = summary; } - const doc = await makePing(es, monitorId, pingFields, mogrify); + const doc = await makePing(es, monitorId, pingFields, mogrify, false); docs.push(doc); // @ts-ignore summary[doc.monitor.status]++; } + + if (refresh) { + es.indices.refresh(); + } + return docs; }; @@ -152,7 +159,8 @@ export const makeChecks = async ( numIps: number, every: number, // number of millis between checks fields: { [key: string]: any } = {}, - mogrify: (doc: any) => any = d => d + mogrify: (doc: any) => any = d => d, + refresh: boolean = true ) => { const checks = []; const oldestTime = new Date().getTime() - numChecks * every; @@ -169,7 +177,11 @@ export const makeChecks = async ( }, }, }); - checks.push(await makeCheck(es, monitorId, numIps, fields, mogrify)); + checks.push(await makeCheck(es, monitorId, numIps, fields, mogrify, false)); + } + + if (refresh) { + es.indices.refresh(); } return checks; @@ -183,19 +195,29 @@ export const makeChecksWithStatus = async ( every: number, fields: { [key: string]: any } = {}, status: 'up' | 'down', - mogrify: (doc: any) => any = d => d + mogrify: (doc: any) => any = d => d, + refresh: boolean = true ) => { const oppositeStatus = status === 'up' ? 'down' : 'up'; - return await makeChecks(es, monitorId, numChecks, numIps, every, fields, d => { - d.monitor.status = status; - if (d.summary) { - d.summary[status] += d.summary[oppositeStatus]; - d.summary[oppositeStatus] = 0; - } - - return mogrify(d); - }); + return await makeChecks( + es, + monitorId, + numChecks, + numIps, + every, + fields, + d => { + d.monitor.status = status; + if (d.summary) { + d.summary[status] += d.summary[oppositeStatus]; + d.summary[oppositeStatus] = 0; + } + + return mogrify(d); + }, + refresh + ); }; // Helper for processing a list of checks to find the time picker bounds. diff --git a/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts b/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts index b0d97837c770f9..20fe59d149ae8f 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts @@ -34,66 +34,69 @@ export default function({ getService }: FtrProviderContext) { let dateRange: { start: string; end: string }; [true, false].forEach(async (includeTimespan: boolean) => { - describe(`with timespans ${includeTimespan ? 'included' : 'missing'}`, async () => { - before(async () => { - const promises: Array> = []; - - // When includeTimespan is false we have to remove the values there. - let mogrify = (d: any) => d; - if ((includeTimespan = false)) { - mogrify = (d: any): any => { - d.monitor.delete('timespan'); + [true, false].forEach(async (includeObserver: boolean) => { + describe(`with timespans=${includeTimespan} and observer=${includeObserver}`, async () => { + before(async () => { + const promises: Array> = []; + + const mogrify = (d: any) => { + if (!includeTimespan) { + delete d.monitor.timespan; + } + if (!includeObserver) { + delete d.observer; + } return d; }; - } - - const makeMonitorChecks = async (monitorId: string, status: 'up' | 'down') => { - return makeChecksWithStatus( - getService('legacyEs'), - monitorId, - checksPerMonitor, - numIps, - scheduleEvery, - {}, - status, - mogrify - ); - }; - for (let i = 0; i < numUpMonitors; i++) { - promises.push(makeMonitorChecks(`up-${i}`, 'up')); - } - for (let i = 0; i < numDownMonitors; i++) { - promises.push(makeMonitorChecks(`down-${i}`, 'down')); - } + const makeMonitorChecks = async (monitorId: string, status: 'up' | 'down') => { + return makeChecksWithStatus( + getService('legacyEs'), + monitorId, + checksPerMonitor, + numIps, + scheduleEvery, + {}, + status, + mogrify + ); + }; - const allResults = await Promise.all(promises); - dateRange = getChecksDateRange(allResults); - }); + for (let i = 0; i < numUpMonitors; i++) { + promises.push(makeMonitorChecks(`up-${i}`, 'up')); + } + for (let i = 0; i < numDownMonitors; i++) { + promises.push(makeMonitorChecks(`down-${i}`, 'down')); + } - it('will count all statuses correctly', async () => { - const apiResponse = await supertest.get( - `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}` - ); + const allResults = await Promise.all(promises); + dateRange = getChecksDateRange(allResults); + }); - expectFixtureEql(apiResponse.body, 'snapshot'); - }); + it('will count all statuses correctly', async () => { + const apiResponse = await supertest.get( + `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}` + ); - it('will fetch a monitor snapshot filtered by down status', async () => { - const statusFilter = 'down'; - const apiResponse = await supertest.get( - `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` - ); + expectFixtureEql(apiResponse.body, 'snapshot'); + }); - expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_down'); - }); + it('will fetch a monitor snapshot filtered by down status', async () => { + const statusFilter = 'down'; + const apiResponse = await supertest.get( + `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` + ); - it('will fetch a monitor snapshot filtered by up status', async () => { - const statusFilter = 'up'; - const apiResponse = await supertest.get( - `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` - ); - expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_up'); + expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_down'); + }); + + it('will fetch a monitor snapshot filtered by up status', async () => { + const statusFilter = 'up'; + const apiResponse = await supertest.get( + `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}` + ); + expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_up'); + }); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts index 79a1e667e5458b..a1cb60483c332d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts @@ -74,6 +74,17 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + it('should report that it failed to import a thousand and one (10001) simple rules', async () => { + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', getSimpleRuleAsNdjson(new Array(10001).fill('rule-1')), 'rules.ndjson') + .query() + .expect(500); + + expect(body).to.eql({ message: "Can't import more than 10000 rules", status_code: 500 }); + }); + it('should be able to read an imported rule back out correctly', async () => { await supertest .post(`${DETECTION_ENGINE_RULES_URL}/_import`) diff --git a/x-pack/test/functional/apps/apm/index.ts b/x-pack/test/functional/apps/apm/index.ts index 945af09183f03d..bf254f9b9b4193 100644 --- a/x-pack/test/functional/apps/apm/index.ts +++ b/x-pack/test/functional/apps/apm/index.ts @@ -6,7 +6,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { - describe('APM', function() { + describe('APM specs', function() { this.tags('ciGroup6'); loadTestFile(require.resolve('./feature_controls')); }); diff --git a/x-pack/test/functional/apps/infra/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs_source_configuration.ts index 1dfbe3526ce409..ecad5a40ec42e9 100644 --- a/x-pack/test/functional/apps/infra/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs_source_configuration.ts @@ -15,8 +15,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'infraLogs']); const retry = getService('retry'); - // FLAKY: https://github.com/elastic/kibana/issues/58059 - describe.skip('Logs Source Configuration', function() { + describe('Logs Source Configuration', function() { this.tags('smoke'); before(async () => { diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts index 798a04cae37405..1bcdeef394c002 100644 --- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts +++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts @@ -36,7 +36,7 @@ export default function({ getService }: FtrProviderContext) { }, dependentVariable: 'y', trainingPercent: '20', - modelMemory: '105mb', + modelMemory: '200mb', createIndexPattern: true, expected: { row: { diff --git a/x-pack/test/functional/services/infra_source_configuration_form.ts b/x-pack/test/functional/services/infra_source_configuration_form.ts index ab61d5232fa1c7..dbae6f00f75a22 100644 --- a/x-pack/test/functional/services/infra_source_configuration_form.ts +++ b/x-pack/test/functional/services/infra_source_configuration_form.ts @@ -36,25 +36,37 @@ export function InfraSourceConfigurationFormProvider({ getService }: FtrProvider return await testSubjects.find('~addLogColumnPopover'); }, async addTimestampLogColumn() { - await (await this.getAddLogColumnButton()).click(); + // try to open the popover + const popover = await retry.try(async () => { + await (await this.getAddLogColumnButton()).click(); + return this.getAddLogColumnPopover(); + }); + + // try to select the timestamp field await retry.try(async () => { - await ( - await testSubjects.findDescendant( - '~addTimestampLogColumn', - await this.getAddLogColumnPopover() - ) - ).click(); + await (await testSubjects.findDescendant('~addTimestampLogColumn', popover)).click(); }); + + // wait for timestamp panel to show up + await testSubjects.findDescendant('~systemLogColumnPanel:Timestamp', await this.getForm()); }, async addFieldLogColumn(fieldName: string) { - await (await this.getAddLogColumnButton()).click(); + // try to open the popover + const popover = await retry.try(async () => { + await (await this.getAddLogColumnButton()).click(); + return this.getAddLogColumnPopover(); + }); + + // try to select the given field await retry.try(async () => { - const popover = await this.getAddLogColumnPopover(); await (await testSubjects.findDescendant('~fieldSearchInput', popover)).type(fieldName); await ( await testSubjects.findDescendant(`~addFieldLogColumn:${fieldName}`, popover) ).click(); }); + + // wait for field panel to show up + await testSubjects.findDescendant(`~fieldLogColumnPanel:${fieldName}`, await this.getForm()); }, async getLogColumnPanels(): Promise { return await testSubjects.findAllDescendant('~logColumnPanel', await this.getForm());