From ae663eb7d07618b1c3233e63d3331a5c6c66ad59 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Fri, 15 May 2020 14:31:12 -0500 Subject: [PATCH 01/14] [ML] Add linking to dataframe from job management tab (#65778) * [ML] Add linking to dataframe from job management tab * [ML] Add linking to df analytics job list from management tab * [ML] Add ability to query text/job id from url for DFA page * [ML] Add linking to dataframe from job management tab * [ML] Add linking to df analytics job list from management tab * [ML] Add ability to query text/job id from url for DFA page * [ML] Refactor get_job_id_url util function & clean up * [ML] Add declaration file for job_list's utils Co-authored-by: Elastic Machine --- .../analytics_list/analytics_list.tsx | 23 ++++++++++++++++++- .../components/analytics_list/columns.tsx | 11 +++++++-- .../components/jobs_list/jobs_list.js | 4 ++-- .../jobs/jobs_list/components/utils.d.ts | 7 ++++++ .../jobs/jobs_list/components/utils.js | 14 +---------- .../public/application/util/get_job_id_url.ts | 20 ++++++++++++++++ 6 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts create mode 100644 x-pack/plugins/ml/public/application/util/get_job_id_url.ts diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 01cce153ce494..68e728c019873 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC, useState } from 'react'; +import React, { Fragment, FC, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; @@ -15,6 +15,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, + EuiSearchBar, } from '@elastic/eui'; import { @@ -51,6 +52,7 @@ import { RefreshAnalyticsListButton } from '../refresh_analytics_list_button'; import { CreateAnalyticsButton } from '../create_analytics_button'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; import { CreateAnalyticsFlyoutWrapper } from '../create_analytics_flyout_wrapper'; +import { getSelectedJobIdFromUrl } from '../../../../../jobs/jobs_list/components/utils'; function getItemIdToExpandedRowMap( itemIds: DataFrameAnalyticsId[], @@ -91,6 +93,8 @@ export const DataFrameAnalyticsList: FC = ({ const [isLoading, setIsLoading] = useState(false); const [filterActive, setFilterActive] = useState(false); + const [queryText, setQueryText] = useState(''); + const [analytics, setAnalytics] = useState([]); const [analyticsStats, setAnalyticsStats] = useState( undefined @@ -107,6 +111,7 @@ export const DataFrameAnalyticsList: FC = ({ const [sortField, setSortField] = useState(DataFrameAnalyticsListColumn.id); const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASC); + const [jobIdSelected, setJobIdSelected] = useState(false); const disabled = !checkPermission('canCreateDataFrameAnalytics') || !checkPermission('canStartStopDataFrameAnalytics'); @@ -119,6 +124,20 @@ export const DataFrameAnalyticsList: FC = ({ blockRefresh ); + // Query text/job_id based on url but only after getAnalytics is done first + // jobIdSelected makes sure the query is only run once since analytics is being refreshed constantly + const selectedId = getSelectedJobIdFromUrl(window.location.href); + useEffect(() => { + if (jobIdSelected === false && analytics.length > 0) { + if (selectedId !== undefined) { + setJobIdSelected(true); + setQueryText(selectedId); + const selectedIdQuery: Query = EuiSearchBar.Query.parse(selectedId); + onQueryChange({ query: selectedIdQuery, error: undefined }); + } + } + }, [jobIdSelected, analytics]); + // Subscribe to the refresh observable to trigger reloading the analytics list. useRefreshAnalyticsList({ isLoading: setIsLoading, @@ -134,6 +153,7 @@ export const DataFrameAnalyticsList: FC = ({ clauses = query.ast.clauses; } if (clauses.length > 0) { + setQueryText(query.text); setFilterActive(true); filterAnalytics(clauses as Array); } else { @@ -297,6 +317,7 @@ export const DataFrameAnalyticsList: FC = ({ }; const search = { + query: queryText, onChange: onQueryChange, box: { incremental: true, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx index 19b51f7615345..236a8083a95e6 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx @@ -16,8 +16,10 @@ import { EuiScreenReaderOnly, EuiText, EuiToolTip, + EuiLink, RIGHT_ALIGNMENT, } from '@elastic/eui'; +import { getJobIdUrl } from '../../../../../util/get_job_id_url'; import { getAnalysisType, DataFrameAnalyticsId } from '../../../../common'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; @@ -135,6 +137,10 @@ export const progressColumn = { 'data-test-subj': 'mlAnalyticsTableColumnProgress', }; +export const getDFAnalyticsJobIdLink = (item: DataFrameAnalyticsListRow) => ( + {item.id} +); + export const getColumns = ( expandedRowItemIds: DataFrameAnalyticsId[], setExpandedRowItemIds: React.Dispatch>, @@ -193,12 +199,13 @@ export const getColumns = ( 'data-test-subj': 'mlAnalyticsTableRowDetailsToggle', }, { - field: DataFrameAnalyticsListColumn.id, name: 'ID', - sortable: true, + sortable: (item: DataFrameAnalyticsListRow) => item.id, truncateText: true, 'data-test-subj': 'mlAnalyticsTableColumnId', scope: 'row', + render: (item: DataFrameAnalyticsListRow) => + isManagementTable ? getDFAnalyticsJobIdLink(item) : item.id, }, { field: DataFrameAnalyticsListColumn.description, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 7036b4f64b3c5..9874ac56577d3 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -14,7 +14,7 @@ import { toLocaleString } from '../../../../util/string_utils'; import { ResultLinks, actionsMenuContent } from '../job_actions'; import { JobDescription } from './job_description'; import { JobIcon } from '../../../../components/job_message_icon'; -import { getJobIdUrl } from '../utils'; +import { getJobIdUrl } from '../../../../util/get_job_id_url'; import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -71,7 +71,7 @@ export class JobsList extends Component { return id; } - return {id}; + return {id}; } getPageOfJobs(index, size, sortField, sortDirection) { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts new file mode 100644 index 0000000000000..5f72d155cbd5a --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export function getSelectedJobIdFromUrl(str: string): string; +export function clearSelectedJobIdFromUrl(str: string): void; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index 1f2a57f999775..4f77004b91f99 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -10,7 +10,7 @@ import rison from 'rison-node'; import { mlJobService } from '../../../services/job_service'; import { ml } from '../../../services/ml_api_service'; -import { getToastNotifications, getBasePath } from '../../../util/dependency_cache'; +import { getToastNotifications } from '../../../util/dependency_cache'; import { JOB_STATE, DATAFEED_STATE } from '../../../../../common/constants/states'; import { parseInterval } from '../../../../../common/util/parse_interval'; import { i18n } from '@kbn/i18n'; @@ -367,18 +367,6 @@ function jobProperty(job, prop) { return job[propMap[prop]]; } -export function getJobIdUrl(jobId) { - // Create url for filtering by job id for kibana management table - const settings = { - jobId, - }; - const encoded = rison.encode(settings); - const url = `?mlManagement=${encoded}`; - const basePath = getBasePath(); - - return `${basePath.get()}/app/ml#/jobs${url}`; -} - function getUrlVars(url) { const vars = {}; url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(_, key, value) { diff --git a/x-pack/plugins/ml/public/application/util/get_job_id_url.ts b/x-pack/plugins/ml/public/application/util/get_job_id_url.ts new file mode 100644 index 0000000000000..a6ca575f21b50 --- /dev/null +++ b/x-pack/plugins/ml/public/application/util/get_job_id_url.ts @@ -0,0 +1,20 @@ +/* + * 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 rison from 'rison-node'; + +import { getBasePath } from './dependency_cache'; + +export function getJobIdUrl(tabId: string, jobId: string): string { + // Create url for filtering by job id for kibana management table + const settings = { + jobId, + }; + const encoded = rison.encode(settings); + const url = `?mlManagement=${encoded}`; + const basePath = getBasePath(); + + return `${basePath.get()}/app/ml#/${tabId}${url}`; +} From 7f7585de27717a064fc033abe51b5ece887fd2ed Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Fri, 15 May 2020 14:39:22 -0500 Subject: [PATCH 02/14] [APM] Disable map layout animation (#66763) * [APM] Disable map layout animation Don't animate the layout. Keep animation for centering/zoom. `preventDefault` when centering an already focused node so it doesn't do an unnecessary navigation. Fixes #66695 --- .../components/app/ServiceMap/Cytoscape.tsx | 3 --- .../app/ServiceMap/Popover/Contents.tsx | 4 +-- .../app/ServiceMap/Popover/index.tsx | 27 ++++++++++++------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx index 5d1ca923cbc8f..67c6cd9978bf1 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx @@ -81,9 +81,6 @@ function getLayoutOptions( fit: true, padding: nodeHeight, spacingFactor: 0.85, - animate: true, - animationEasing: animationOptions.easing, - animationDuration: animationOptions.duration, // @ts-ignore // Rotate nodes counter-clockwise to transform layout from top→bottom to left→right. // The extra 5° achieves the effect of separating overlapping taxi-styled edges. diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx index b5bfa63c1bdde..e58d9a0ff761a 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx @@ -15,7 +15,7 @@ import { import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import cytoscape from 'cytoscape'; -import React from 'react'; +import React, { MouseEvent } from 'react'; import styled from 'styled-components'; import { fontSize, px } from '../../../../style/variables'; import { Buttons } from './Buttons'; @@ -31,7 +31,7 @@ const popoverMinWidth = 280; interface ContentsProps { isService: boolean; label: string; - onFocusClick: () => void; + onFocusClick: (event: MouseEvent) => void; selectedNodeData: cytoscape.NodeDataDefinition; selectedNodeServiceName: string; } diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx index 1c9d5092bfcf5..86ef0b880c4da 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx @@ -8,6 +8,7 @@ import { EuiPopover } from '@elastic/eui'; import cytoscape from 'cytoscape'; import React, { CSSProperties, + MouseEvent, useCallback, useContext, useEffect, @@ -16,8 +17,8 @@ import React, { } from 'react'; import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames'; import { CytoscapeContext } from '../Cytoscape'; -import { Contents } from './Contents'; import { animationOptions } from '../cytoscapeOptions'; +import { Contents } from './Contents'; interface PopoverProps { focusedServiceName?: string; @@ -87,14 +88,18 @@ export function Popover({ focusedServiceName }: PopoverProps) { } }, [popoverRef, x, y]); - const centerSelectedNode = useCallback(() => { - if (cy) { - cy.animate({ - ...animationOptions, - center: { eles: cy.getElementById(selectedNodeServiceName) } - }); - } - }, [cy, selectedNodeServiceName]); + const centerSelectedNode = useCallback( + (event: MouseEvent) => { + event.preventDefault(); + if (cy) { + cy.animate({ + ...animationOptions, + center: { eles: cy.getElementById(selectedNodeServiceName) } + }); + } + }, + [cy, selectedNodeServiceName] + ); const isAlreadyFocused = focusedServiceName === selectedNodeServiceName; @@ -110,7 +115,9 @@ export function Popover({ focusedServiceName }: PopoverProps) { deselect() + } selectedNodeData={selectedNodeData} selectedNodeServiceName={selectedNodeServiceName} /> From 91ed1dd87ca5008efc92699ba74c3bbea57aa099 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 15 May 2020 22:05:54 +0200 Subject: [PATCH 03/14] allow any type for customResponseHeaders config (#66689) * allow any type of value for customResponseHeaders and convert them * fix test config creation * add `?? {}` to avoid breaking all tests... --- .../http/cookie_session_storage.test.ts | 1 + src/core/server/http/http_config.test.ts | 47 ++++++++++++++++++- src/core/server/http/http_config.ts | 18 +++++-- src/core/server/http/test_utils.ts | 1 + 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/core/server/http/cookie_session_storage.test.ts b/src/core/server/http/cookie_session_storage.test.ts index 4ce422e1f65c4..0ca87eae6e235 100644 --- a/src/core/server/http/cookie_session_storage.test.ts +++ b/src/core/server/http/cookie_session_storage.test.ts @@ -62,6 +62,7 @@ configService.atPath.mockReturnValue( disableProtection: true, whitelist: [], }, + customResponseHeaders: {}, } as any) ); diff --git a/src/core/server/http/http_config.test.ts b/src/core/server/http/http_config.test.ts index 7ac707b0f3d83..0976bfd56682e 100644 --- a/src/core/server/http/http_config.test.ts +++ b/src/core/server/http/http_config.test.ts @@ -18,7 +18,8 @@ */ import uuid from 'uuid'; -import { config } from '.'; +import { config, HttpConfig } from './http_config'; +import { CspConfig } from '../csp'; const validHostnames = ['www.example.com', '8.8.8.8', '::1', 'localhost']; const invalidHostname = 'asdf$%^'; @@ -107,6 +108,23 @@ test('throws if xsrf.whitelist element does not start with a slash', () => { ); }); +test('accepts any type of objects for custom headers', () => { + const httpSchema = config.schema; + const obj = { + customResponseHeaders: { + string: 'string', + bool: true, + number: 12, + array: [1, 2, 3], + nested: { + foo: 1, + bar: 'dolly', + }, + }, + }; + expect(() => httpSchema.validate(obj)).not.toThrow(); +}); + describe('with TLS', () => { test('throws if TLS is enabled but `redirectHttpFromPort` is equal to `port`', () => { const httpSchema = config.schema; @@ -173,3 +191,30 @@ describe('with compression', () => { expect(() => httpSchema.validate(obj)).toThrowErrorMatchingSnapshot(); }); }); + +describe('HttpConfig', () => { + it('converts customResponseHeaders to strings or arrays of strings', () => { + const httpSchema = config.schema; + const rawConfig = httpSchema.validate({ + customResponseHeaders: { + string: 'string', + bool: true, + number: 12, + array: [1, 2, 3], + nested: { + foo: 1, + bar: 'dolly', + }, + }, + }); + const httpConfig = new HttpConfig(rawConfig, CspConfig.DEFAULT); + + expect(httpConfig.customResponseHeaders).toEqual({ + string: 'string', + bool: 'true', + number: '12', + array: ['1', '2', '3'], + nested: '{"foo":1,"bar":"dolly"}', + }); + }); +}); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 73f44f3c5ab5c..7c72e3270743e 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -57,7 +57,7 @@ export const config = { ), schema.boolean({ defaultValue: false }) ), - customResponseHeaders: schema.recordOf(schema.string(), schema.string(), { + customResponseHeaders: schema.recordOf(schema.string(), schema.any(), { defaultValue: {}, }), host: schema.string({ @@ -136,7 +136,7 @@ export class HttpConfig { public socketTimeout: number; public port: number; public cors: boolean | { origin: string[] }; - public customResponseHeaders: Record; + public customResponseHeaders: Record; public maxPayload: ByteSizeValue; public basePath?: string; public rewriteBasePath: boolean; @@ -153,7 +153,15 @@ export class HttpConfig { this.host = rawHttpConfig.host; this.port = rawHttpConfig.port; this.cors = rawHttpConfig.cors; - this.customResponseHeaders = rawHttpConfig.customResponseHeaders; + this.customResponseHeaders = Object.entries(rawHttpConfig.customResponseHeaders ?? {}).reduce( + (headers, [key, value]) => { + return { + ...headers, + [key]: Array.isArray(value) ? value.map(e => convertHeader(e)) : convertHeader(value), + }; + }, + {} + ); this.maxPayload = rawHttpConfig.maxPayload; this.name = rawHttpConfig.name; this.basePath = rawHttpConfig.basePath; @@ -166,3 +174,7 @@ export class HttpConfig { this.xsrf = rawHttpConfig.xsrf; } } + +const convertHeader = (entry: any): string => { + return typeof entry === 'object' ? JSON.stringify(entry) : String(entry); +}; diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts index 49c4d690c6876..0e639aa72a825 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -45,6 +45,7 @@ configService.atPath.mockReturnValue( disableProtection: true, whitelist: [], }, + customResponseHeaders: {}, } as any) ); From ab2600f823f04740ef3c0a9de4312eea5fd84236 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Fri, 15 May 2020 14:17:18 -0600 Subject: [PATCH 04/14] [SIEM][Lists] Adds 90% of the REST API and client API for exception lists and exception items ## Summary See for more details: https://github.com/elastic/kibana/issues/65938 Adds pieces of the `exception list` and `exception list item` and refactors/cleans the code up where I had parts incorrect with little things such as the javascript library io-ts. Some unit tests were added but I am holding off until more of the operations solidify before adding the unit tests. Everything is still behind a feature flag that must be enabled and not advised still at this point to use so I feel ok pushing these parts forward. Adds to the API: - Create exception list - Read exception list - Update exception list - Delete exception list (and exception list items that are associated with it) - Create exception list item - Find exception list (/_find) - Read exception list item - Update exception list item - Delete exception list items individually - Find exception list item (/_find) What is still missing from the REST and client API? - Patch exception list - Patch exception list item - Bulk versions of everything - Import/Export options for these exception lists and list items ### Manual testing and REST API endpoints Go here: ```sh /projects/kibana/x-pack/plugins/lists/server/scripts ``` See the files: ```sh delete_all_exception_lists.sh delete_exception_list.sh delete_exception_list_by_id.sh delete_exception_list_item.sh delete_exception_list_item_by_id.sh exception_lists find_exception_list_items.sh find_exception_lists.sh get_exception_list.sh get_exception_list_by_id.sh get_exception_list_item.sh get_exception_list_item_by_id.sh post_exception_list.sh post_exception_list_item.sh update_exception_list.sh update_exception_list_item.sh ``` Ensure you first run: ```sh ./hard_reset ``` and ensure you have setup your kibana.dev.yml to have: ```yml # Enable lists feature xpack.lists.enabled: true xpack.lists.listIndex: '.lists-frank' xpack.lists.listItemIndex: '.items-frank' ``` Then you can use the above scripts to create, read, update, and delete exception list and exception list items as well as perform find commands against them all. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios Note: Some but limited unit tests at this point. --- x-pack/plugins/lists/common/constants.ts | 10 +- .../lists/common/schemas/common/schemas.ts | 46 +++- .../elastic_query/update_es_list_schema.ts | 1 + x-pack/plugins/lists/common/schemas/index.ts | 6 +- .../create_exception_list_item_schema.ts | 65 +++++ .../create_exception_list_schema.mock.ts | 19 ++ .../create_exception_list_schema.test.ts | 96 +++++++ .../request/create_exception_list_schema.ts | 55 ++++ .../request/create_list_item_schema.ts | 4 +- .../schemas/request/create_list_schema.ts | 4 +- .../delete_exception_list_item_schema.ts | 20 ++ .../request/delete_exception_list_schema.ts | 20 ++ .../request/delete_list_item_schema.ts | 4 +- .../find_exception_list_item_schema.ts | 35 +++ .../request/find_exception_list_schema.ts | 28 ++ .../request/import_list_item_query_schema.ts | 6 +- .../lists/common/schemas/request/index.ts | 11 + .../schemas/request/patch_list_item_schema.ts | 4 +- .../schemas/request/patch_list_schema.ts | 6 +- .../read_exception_list_item_schema.ts | 20 ++ .../request/read_exception_list_schema.ts | 20 ++ .../schemas/request/read_list_item_schema.ts | 6 +- .../update_exception_list_item_schema.ts | 63 +++++ .../request/update_exception_list_schema.ts | 53 ++++ .../request/update_list_item_schema.ts | 8 +- .../schemas/request/update_list_schema.ts | 8 +- .../response/exception_list_item_schema.ts | 52 ++++ .../schemas/response/exception_list_schema.ts | 46 ++++ .../found_exception_list_item_schema.ts | 24 ++ .../response/found_exception_list_schema.ts | 24 ++ .../lists/common/schemas/response/index.ts | 4 + .../exceptions_list_so_schema.ts | 50 ++++ .../common/schemas/saved_objects/index.ts | 6 + .../schemas/types/default_entries_array.ts | 28 ++ .../schemas/types/default_string_array.ts | 22 ++ .../common/schemas/types/default_uuid.ts | 26 ++ .../lists/common/schemas/types/entries.ts | 21 ++ .../lists/common/schemas/types/index.ts | 10 + .../plugins/lists/common/siem_common_deps.ts | 6 +- x-pack/plugins/lists/server/plugin.ts | 26 +- .../create_exception_list_item_route.ts | 109 ++++++++ .../routes/create_exception_list_route.ts | 82 ++++++ .../delete_exception_list_item_route.ts | 70 +++++ .../routes/delete_exception_list_route.ts | 71 +++++ .../server/routes/delete_list_item_route.ts | 2 +- .../routes/find_exception_list_item_route.ts | 73 +++++ .../routes/find_exception_list_route.ts | 65 +++++ x-pack/plugins/lists/server/routes/index.ts | 12 + .../lists/server/routes/init_routes.ts | 31 ++- .../routes/read_exception_list_item_route.ts | 68 +++++ .../routes/read_exception_list_route.ts | 68 +++++ .../server/routes/read_list_item_route.ts | 2 +- .../update_exception_list_item_route.ts | 89 +++++++ .../routes/update_exception_list_route.ts | 83 ++++++ .../utils/get_error_message_exception_list.ts | 21 ++ .../get_error_message_exception_list_item.ts | 21 ++ .../routes/utils/get_exception_list_client.ts | 19 ++ .../server/routes/utils/get_list_client.ts | 2 +- .../lists/server/routes/utils/index.ts | 3 + .../server/saved_objects/exception_list.ts | 115 ++++++++ .../lists/server/saved_objects/index.ts | 7 + .../saved_objects/init_saved_objects.ts | 14 + .../scripts/delete_all_exception_lists.sh | 33 +++ .../server/scripts/delete_exception_list.sh | 16 ++ .../scripts/delete_exception_list_by_id.sh | 16 ++ .../scripts/delete_exception_list_item.sh | 16 ++ .../delete_exception_list_item_by_id.sh | 16 ++ .../lists/server/scripts/delete_list.sh | 2 +- .../exception_lists/new/exception_list.json | 8 + .../new/exception_list_auto_id.json | 5 + .../new/exception_list_item.json | 21 ++ .../new/exception_list_item_auto_id.json | 20 ++ .../updates/simple_update.json | 8 + .../updates/simple_update_item.json | 15 ++ .../scripts/find_exception_list_items.sh | 16 ++ .../server/scripts/find_exception_lists.sh | 15 ++ .../server/scripts/get_exception_list.sh | 15 ++ .../scripts/get_exception_list_by_id.sh | 15 ++ .../server/scripts/get_exception_list_item.sh | 15 ++ .../scripts/get_exception_list_item_by_id.sh | 15 ++ .../lists/server/scripts/hard_reset.sh | 3 + .../server/scripts/post_exception_list.sh | 30 +++ .../scripts/post_exception_list_item.sh | 30 +++ .../server/scripts/update_exception_list.sh | 30 +++ .../scripts/update_exception_list_item.sh | 30 +++ .../exception_lists/create_exception_list.ts | 72 +++++ .../create_exception_list_item.ts | 81 ++++++ .../exception_lists/delete_exception_list.ts | 42 +++ .../delete_exception_list_item.ts | 41 +++ .../delete_exception_list_items_by_list.ts | 89 +++++++ .../exception_lists/exception_list_client.ts | 250 ++++++++++++++++++ .../exception_list_client_types.ts | 130 +++++++++ .../exception_lists/find_exception_list.ts | 58 ++++ .../find_exception_list_item.ts | 77 ++++++ .../exception_lists/get_exception_list.ts | 64 +++++ .../get_exception_list_item.ts | 66 +++++ .../server/services/exception_lists/index.ts | 16 ++ .../server/services/exception_lists/types.ts | 6 + .../exception_lists/update_exception_list.ts | 74 ++++++ .../update_exception_list_item.ts | 87 ++++++ .../server/services/exception_lists/utils.ts | 235 ++++++++++++++++ .../lists/{client.ts => list_client.ts} | 2 +- .../{client_types.ts => list_client_types.ts} | 0 .../lists/server/services/utils/index.ts | 2 +- .../plugins/lists/server/siem_server_deps.ts | 35 +-- x-pack/plugins/lists/server/types.ts | 23 +- .../exact_check.test.ts | 2 +- .../exact_check.ts | 0 .../format_errors.test.ts | 0 .../format_errors.ts | 0 .../utils.ts => common/test_utils.ts} | 2 +- x-pack/plugins/siem/server/index.ts | 14 + .../detection_engine/routes/rules/validate.ts | 4 +- .../response/check_type_dependents.test.ts | 4 +- .../schemas/response/error_schema.test.ts | 4 +- .../response/find_rules_schema.test.ts | 4 +- .../response/import_rules_schema.test.ts | 4 +- .../response/prepackaged_rules_schema.test.ts | 4 +- .../prepackaged_rules_status_schema.test.ts | 4 +- .../response/rules_bulk_schema.test.ts | 4 +- .../schemas/response/rules_schema.test.ts | 4 +- .../type_timeline_only_schema.test.ts | 4 +- .../schemas/types/iso_date_string.test.ts | 2 +- .../schemas/types/lists_default_array.test.ts | 2 +- ...positive_integer_greater_than_zero.test.ts | 2 +- .../schemas/types/postive_integer.test.ts | 2 +- .../types/references_default_array.test.ts | 2 +- .../routes/schemas/types/risk_score.test.ts | 2 +- .../routes/schemas/types/uuid.test.ts | 2 +- .../build_validation/route_validation.ts | 4 +- 130 files changed, 3800 insertions(+), 106 deletions(-) create mode 100644 x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts create mode 100644 x-pack/plugins/lists/common/schemas/saved_objects/index.ts create mode 100644 x-pack/plugins/lists/common/schemas/types/default_entries_array.ts create mode 100644 x-pack/plugins/lists/common/schemas/types/default_string_array.ts create mode 100644 x-pack/plugins/lists/common/schemas/types/default_uuid.ts create mode 100644 x-pack/plugins/lists/common/schemas/types/entries.ts create mode 100644 x-pack/plugins/lists/common/schemas/types/index.ts create mode 100644 x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts create mode 100644 x-pack/plugins/lists/server/routes/create_exception_list_route.ts create mode 100644 x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts create mode 100644 x-pack/plugins/lists/server/routes/delete_exception_list_route.ts create mode 100644 x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts create mode 100644 x-pack/plugins/lists/server/routes/find_exception_list_route.ts create mode 100644 x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts create mode 100644 x-pack/plugins/lists/server/routes/read_exception_list_route.ts create mode 100644 x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts create mode 100644 x-pack/plugins/lists/server/routes/update_exception_list_route.ts create mode 100644 x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts create mode 100644 x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts create mode 100644 x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts create mode 100644 x-pack/plugins/lists/server/saved_objects/exception_list.ts create mode 100644 x-pack/plugins/lists/server/saved_objects/index.ts create mode 100644 x-pack/plugins/lists/server/saved_objects/init_saved_objects.ts create mode 100755 x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh create mode 100755 x-pack/plugins/lists/server/scripts/delete_exception_list.sh create mode 100755 x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh create mode 100755 x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh create mode 100755 x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh create mode 100644 x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json create mode 100644 x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json create mode 100644 x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json create mode 100644 x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json create mode 100644 x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json create mode 100644 x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json create mode 100755 x-pack/plugins/lists/server/scripts/find_exception_list_items.sh create mode 100755 x-pack/plugins/lists/server/scripts/find_exception_lists.sh create mode 100755 x-pack/plugins/lists/server/scripts/get_exception_list.sh create mode 100755 x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh create mode 100755 x-pack/plugins/lists/server/scripts/get_exception_list_item.sh create mode 100755 x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh create mode 100755 x-pack/plugins/lists/server/scripts/post_exception_list.sh create mode 100755 x-pack/plugins/lists/server/scripts/post_exception_list_item.sh create mode 100755 x-pack/plugins/lists/server/scripts/update_exception_list.sh create mode 100755 x-pack/plugins/lists/server/scripts/update_exception_list_item.sh create mode 100644 x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/index.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/types.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/utils.ts rename x-pack/plugins/lists/server/services/lists/{client.ts => list_client.ts} (99%) rename x-pack/plugins/lists/server/services/lists/{client_types.ts => list_client_types.ts} (100%) rename x-pack/plugins/siem/{server/utils/build_validation => common}/exact_check.test.ts (98%) rename x-pack/plugins/siem/{server/utils/build_validation => common}/exact_check.ts (100%) rename x-pack/plugins/siem/{server/utils/build_validation => common}/format_errors.test.ts (100%) rename x-pack/plugins/siem/{server/utils/build_validation => common}/format_errors.ts (100%) rename x-pack/plugins/siem/{server/utils/build_validation/__mocks__/utils.ts => common/test_utils.ts} (95%) diff --git a/x-pack/plugins/lists/common/constants.ts b/x-pack/plugins/lists/common/constants.ts index dbe31fed66413..96d28bf618ce4 100644 --- a/x-pack/plugins/lists/common/constants.ts +++ b/x-pack/plugins/lists/common/constants.ts @@ -5,8 +5,14 @@ */ /** - * Lists routes + * Value list routes */ -export const LIST_URL = `/api/lists`; +export const LIST_URL = '/api/lists'; export const LIST_INDEX = `${LIST_URL}/index`; export const LIST_ITEM_URL = `${LIST_URL}/items`; + +/** + * Exception list routes + */ +export const EXCEPTION_LIST_URL = '/api/exception_lists'; +export const EXCEPTION_LIST_ITEM_URL = '/api/exception_lists/items'; diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.ts b/x-pack/plugins/lists/common/schemas/common/schemas.ts index edc037ed7a0b1..cd69685ffcb1b 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { NonEmptyString } from '../types/non_empty_string'; +import { DefaultStringArray, NonEmptyString } from '../types'; export const name = t.string; export type Name = t.TypeOf; @@ -21,8 +21,9 @@ export const descriptionOrUndefined = t.union([description, t.undefined]); export type DescriptionOrUndefined = t.TypeOf; export const list_id = NonEmptyString; +export type ListId = t.TypeOf; export const list_idOrUndefined = t.union([list_id, t.undefined]); -export type List_idOrUndefined = t.TypeOf; +export type ListIdOrUndefined = t.TypeOf; export const item = t.string; export const created_at = t.string; // TODO: Make this into an ISO Date string check @@ -60,3 +61,44 @@ export type MetaOrUndefined = t.TypeOf; export const esDataTypeUnion = t.union([t.type({ ip }), t.type({ keyword })]); export type EsDataTypeUnion = t.TypeOf; + +export const tags = DefaultStringArray; +export type Tags = t.TypeOf; +export const tagsOrUndefined = t.union([tags, t.undefined]); +export type TagsOrUndefined = t.TypeOf; + +export const _tags = DefaultStringArray; +export type _Tags = t.TypeOf; +export const _tagsOrUndefined = t.union([_tags, t.undefined]); +export type _TagsOrUndefined = t.TypeOf; + +// TODO: Change this into a t.keyof enumeration when we know what types of lists we going to have. +export const exceptionListType = t.string; +export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); +export type ExceptionListType = t.TypeOf; +export type ExceptionListTypeOrUndefined = t.TypeOf; + +// TODO: Change this into a t.keyof enumeration when we know what types of lists we going to have. +export const exceptionListItemType = t.string; +export type ExceptionListItemType = t.TypeOf; + +export const list_type = t.keyof({ item: null, list: null }); +export type ListType = t.TypeOf; + +// TODO: Investigate what the deep structure of a comment is really going to be and then change this to use that deep structure with a default array +export const comment = DefaultStringArray; +export type Comment = t.TypeOf; +export const commentOrUndefined = t.union([comment, t.undefined]); +export type CommentOrUndefined = t.TypeOf; + +export const item_id = NonEmptyString; +export type ItemId = t.TypeOf; +export const itemIdOrUndefined = t.union([item_id, t.undefined]); +export type ItemIdOrUndefined = t.TypeOf; + +export const per_page = t.number; // TODO: Change this out for PositiveNumber from siem +export const total = t.number; // TODO: Change this out for PositiveNumber from siem +export const page = t.number; // TODO: Change this out for PositiveNumber from siem +export const sort_field = t.string; +export const sort_order = t.keyof({ asc: null, desc: null }); +export const filter = t.string; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts index 8f23f3744e563..d008a82308153 100644 --- a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts @@ -16,6 +16,7 @@ import { updated_by, } from '../common/schemas'; +// TODO: Should we use partial here and everywhere these are instead of this OrUndefined? export const updateEsListSchema = t.exact( t.type({ description: descriptionOrUndefined, diff --git a/x-pack/plugins/lists/common/schemas/index.ts b/x-pack/plugins/lists/common/schemas/index.ts index 6a60a6df55691..774378c6318f5 100644 --- a/x-pack/plugins/lists/common/schemas/index.ts +++ b/x-pack/plugins/lists/common/schemas/index.ts @@ -5,7 +5,9 @@ */ export * from './common'; -export * from './request'; -export * from './response'; export * from './elastic_query'; export * from './elastic_response'; +export * from './request'; +export * from './response'; +export * from './saved_objects'; +export * from './types'; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts new file mode 100644 index 0000000000000..f899fd69110fa --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts @@ -0,0 +1,65 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + ItemId, + Tags, + _Tags, + _tags, + comment, + description, + exceptionListItemType, + list_id, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; +import { DefaultEntryArray, DefaultUuid } from '../types'; +import { EntriesArray } from '../types/entries'; + +export const createExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + list_id, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + comment, // defaults to empty array if not set during decode + entries: DefaultEntryArray, // defaults to empty array if not set during decode + item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type CreateExceptionListItemSchemaPartial = Identity< + t.TypeOf +>; +export type CreateExceptionListItemSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays +// and if a item_id is not specified it turns into a default GUID +export type CreateExceptionListItemSchemaDecoded = Identity< + Omit & { + _tags: _Tags; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts new file mode 100644 index 0000000000000..d38d3cc038525 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DESCRIPTION, LIST_ID, META, NAME, TYPE } from '../../constants.mock'; + +import { CreateExceptionListSchema } from './create_exception_list_schema'; + +export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema => ({ + _tags: [], + description: DESCRIPTION, + list_id: LIST_ID, + meta: META, + name: NAME, + tags: [], + type: TYPE, +}); diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts new file mode 100644 index 0000000000000..f19c50c6b42ae --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; + +import { exactCheck, foldLeftRight, getPaths } from '../../siem_common_deps'; + +import { + CreateExceptionListSchema, + createExceptionListSchema, +} from './create_exception_list_schema'; +import { getCreateExceptionListSchemaMock } from './create_exception_list_schema.mock'; + +describe('create_exception_list_schema', () => { + test('it should validate a typical exception lists request', () => { + const payload = getCreateExceptionListSchemaMock(); + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for meta', () => { + const payload = getCreateExceptionListSchemaMock(); + delete payload.meta; + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for tags but return an array', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + const outputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for _tags but return an array', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + const outputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload._tags; + outputPayload._tags = []; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for list_id and auto generate a uuid', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.list_id; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect((message.schema as CreateExceptionListSchema).list_id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); + + test('it should accept an undefined for list_id and generate a correct body not counting the uuid', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.list_id; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListSchema).list_id; + expect(message.schema).toEqual(inputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateExceptionListSchema & { + extraKey?: string; + } = getCreateExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts new file mode 100644 index 0000000000000..5ba3bf4e8f43b --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts @@ -0,0 +1,55 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + ListId, + Tags, + _Tags, + _tags, + description, + exceptionListType, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; +import { DefaultUuid } from '../types/default_uuid'; + +export const createExceptionListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + list_id: DefaultUuid, // defaults to a GUID (UUID v4) string if not set during decode + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type CreateExceptionListSchemaPartial = Identity>; +export type CreateExceptionListSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays. +export type CreateExceptionListSchemaDecoded = Identity< + CreateExceptionListSchema & { + _tags: _Tags; + tags: Tags; + list_id: ListId; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts index 6cba81e47fbcc..6d16f2074864c 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { idOrUndefined, list_id, metaOrUndefined, value } from '../common/schemas'; +import { id, list_id, meta, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const createListItemSchema = t.intersection([ @@ -18,7 +18,7 @@ export const createListItemSchema = t.intersection([ value, }) ), - t.exact(t.partial({ id: idOrUndefined, meta: metaOrUndefined })), + t.exact(t.partial({ id, meta })), ]); export type CreateListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts index 7a6e2a707873c..68df80b2a42dd 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts @@ -6,7 +6,7 @@ import * as t from 'io-ts'; -import { description, idOrUndefined, metaOrUndefined, name, type } from '../common/schemas'; +import { description, id, meta, name, type } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const createListSchema = t.intersection([ @@ -17,7 +17,7 @@ export const createListSchema = t.intersection([ type, }) ), - t.exact(t.partial({ id: idOrUndefined, meta: metaOrUndefined })), + t.exact(t.partial({ id, meta })), ]); export type CreateListSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts new file mode 100644 index 0000000000000..607e05ef8286f --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts @@ -0,0 +1,20 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, item_id } from '../common/schemas'; + +export const deleteExceptionListItemSchema = t.exact( + t.partial({ + id, + item_id, + }) +); + +export type DeleteExceptionListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts new file mode 100644 index 0000000000000..7a6086514f943 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts @@ -0,0 +1,20 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, list_id } from '../common/schemas'; + +export const deleteExceptionListSchema = t.exact( + t.partial({ + id, + list_id, + }) +); + +export type DeleteExceptionListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts index 96f054b304962..91887395e747d 100644 --- a/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { idOrUndefined, list_idOrUndefined, valueOrUndefined } from '../common/schemas'; +import { id, list_id, valueOrUndefined } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const deleteListItemSchema = t.intersection([ @@ -17,7 +17,7 @@ export const deleteListItemSchema = t.intersection([ value: valueOrUndefined, }) ), - t.exact(t.partial({ id: idOrUndefined, list_id: list_idOrUndefined })), + t.exact(t.partial({ id, list_id })), ]); export type DeleteListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts new file mode 100644 index 0000000000000..3fc51dd20b0b3 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts @@ -0,0 +1,35 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { filter, list_id, page, per_page, sort_field, sort_order } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const findExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + list_id, + }) + ), + t.exact( + t.partial({ + filter, // defaults to undefined if not set during decode + page, // defaults to undefined if not set during decode + per_page, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) + ), +]); + +export type FindExceptionListItemSchemaPartial = t.TypeOf; + +export type FindExceptionListItemSchema = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts new file mode 100644 index 0000000000000..f795be9493fbf --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { filter, page, per_page, sort_field, sort_order } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const findExceptionListSchema = t.exact( + t.partial({ + filter, // defaults to undefined if not set during decode + page, // defaults to undefined if not set during decode + per_page, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) +); + +export type FindExceptionListSchemaPartial = t.TypeOf; + +export type FindExceptionListSchema = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts index c1745dda7afab..73d9a53a41e4f 100644 --- a/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts @@ -8,12 +8,10 @@ import * as t from 'io-ts'; -import { list_idOrUndefined, typeOrUndefined } from '../common/schemas'; +import { list_id, type } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; -export const importListItemQuerySchema = t.exact( - t.partial({ list_id: list_idOrUndefined, type: typeOrUndefined }) -); +export const importListItemQuerySchema = t.exact(t.partial({ list_id, type })); export type ImportListItemQuerySchemaPartial = Identity>; export type ImportListItemQuerySchema = RequiredKeepUndefined< diff --git a/x-pack/plugins/lists/common/schemas/request/index.ts b/x-pack/plugins/lists/common/schemas/request/index.ts index d332ab1eb1bab..0dbd9297b773e 100644 --- a/x-pack/plugins/lists/common/schemas/request/index.ts +++ b/x-pack/plugins/lists/common/schemas/request/index.ts @@ -4,16 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ +export * from './create_exception_list_item_schema'; +export * from './create_exception_list_schema'; export * from './create_list_item_schema'; export * from './create_list_schema'; +export * from './delete_exception_list_item_schema'; +export * from './delete_exception_list_schema'; export * from './delete_list_item_schema'; export * from './delete_list_schema'; export * from './export_list_item_query_schema'; +export * from './find_exception_list_item_schema'; +export * from './find_exception_list_schema'; export * from './import_list_item_schema'; export * from './patch_list_item_schema'; export * from './patch_list_schema'; +export * from './read_exception_list_item_schema'; +export * from './read_exception_list_schema'; export * from './read_list_item_schema'; export * from './read_list_schema'; +export * from './update_exception_list_item_schema'; +export * from './update_exception_list_schema'; export * from './import_list_item_query_schema'; export * from './update_list_schema'; +export * from './update_exception_list_schema'; export * from './update_list_item_schema'; diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts index 536931f715f3f..2016069f32fb3 100644 --- a/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { id, metaOrUndefined, valueOrUndefined } from '../common/schemas'; +import { id, meta, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const patchListItemSchema = t.intersection([ @@ -17,7 +17,7 @@ export const patchListItemSchema = t.intersection([ id, }) ), - t.exact(t.partial({ meta: metaOrUndefined, value: valueOrUndefined })), + t.exact(t.partial({ meta, value })), ]); export type PatchListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts index 59d1a66a581a0..653a42dc91653 100644 --- a/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { descriptionOrUndefined, id, metaOrUndefined, nameOrUndefined } from '../common/schemas'; +import { description, id, meta, name } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const patchListSchema = t.intersection([ @@ -17,9 +17,7 @@ export const patchListSchema = t.intersection([ id, }) ), - t.exact( - t.partial({ description: descriptionOrUndefined, meta: metaOrUndefined, name: nameOrUndefined }) - ), + t.exact(t.partial({ description, meta, name })), ]); export type PatchListSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts new file mode 100644 index 0000000000000..095fcd2f63b48 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts @@ -0,0 +1,20 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, item_id } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const readExceptionListItemSchema = t.partial({ + id, + item_id, +}); + +export type ReadExceptionListItemSchemaPartial = t.TypeOf; +export type ReadExceptionListItemSchema = RequiredKeepUndefined; diff --git a/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts new file mode 100644 index 0000000000000..5593e640f71ac --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts @@ -0,0 +1,20 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, list_id } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const readExceptionListSchema = t.partial({ + id, + list_id, +}); + +export type ReadExceptionListSchemaPartial = t.TypeOf; +export type ReadExceptionListSchema = RequiredKeepUndefined; diff --git a/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts index b69523b664fd7..394c1f1e2289a 100644 --- a/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts @@ -8,12 +8,10 @@ import * as t from 'io-ts'; -import { idOrUndefined, list_idOrUndefined, valueOrUndefined } from '../common/schemas'; +import { id, list_id, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; -export const readListItemSchema = t.exact( - t.partial({ id: idOrUndefined, list_id: list_idOrUndefined, value: valueOrUndefined }) -); +export const readListItemSchema = t.exact(t.partial({ id, list_id, value })); export type ReadListItemSchemaPartial = Identity>; export type ReadListItemSchema = RequiredKeepUndefined>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts new file mode 100644 index 0000000000000..162406a6d6589 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts @@ -0,0 +1,63 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + Tags, + _Tags, + _tags, + comment, + description, + exceptionListItemType, + id, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; +import { DefaultEntryArray } from '../types'; +import { EntriesArray } from '../types/entries'; + +export const updateExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + comment, // defaults to empty array if not set during decode + entries: DefaultEntryArray, // defaults to empty array if not set during decode + id, // defaults to undefined if not set during decode + item_id: t.union([t.string, t.undefined]), + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type UpdateExceptionListItemSchemaPartial = Identity< + t.TypeOf +>; +export type UpdateExceptionListItemSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays +// and if a item_id is not specified it turns into a default GUID +export type UpdateExceptionListItemSchemaDecoded = Identity< + Omit & { + _tags: _Tags; + tags: Tags; + entries: EntriesArray; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts new file mode 100644 index 0000000000000..e8a0dcd4994a2 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts @@ -0,0 +1,53 @@ +/* + * 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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + Tags, + _Tags, + _tags, + description, + exceptionListType, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; + +export const updateExceptionListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode + list_id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type UpdateExceptionListSchemaPartial = Identity>; +export type UpdateExceptionListSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays. +export type UpdateExceptionListSchemaDecoded = Identity< + Omit & { + _tags: _Tags; + tags: Tags; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts index 23701ff753bc0..3a42cf28665f5 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { id, metaOrUndefined, value } from '../common/schemas'; +import { id, meta, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const updateListItemSchema = t.intersection([ @@ -18,7 +18,11 @@ export const updateListItemSchema = t.intersection([ value, }) ), - t.exact(t.partial({ meta: metaOrUndefined })), + t.exact( + t.partial({ + meta, // defaults to undefined if not set during decode + }) + ), ]); export type UpdateListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts index 8223a6a34b771..4c5c8c429a14c 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { description, id, metaOrUndefined, name } from '../common/schemas'; +import { description, id, meta, name } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const updateListSchema = t.intersection([ @@ -19,7 +19,11 @@ export const updateListSchema = t.intersection([ name, }) ), - t.exact(t.partial({ meta: metaOrUndefined })), + t.exact( + t.partial({ + meta, // defaults to undefined if not set during decode + }) + ), ]); export type UpdateListSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts new file mode 100644 index 0000000000000..15e1c92c06d13 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + _tags, + commentOrUndefined, + created_at, + created_by, + description, + exceptionListItemType, + id, + item_id, + list_id, + metaOrUndefined, + name, + tags, + tie_breaker_id, + updated_at, + updated_by, +} from '../common/schemas'; +import { entriesArray } from '../types'; + +// TODO: Should we use a partial here to reflect that this can JSON serialize meta, comment as non existent? +export const exceptionListItemSchema = t.exact( + t.type({ + _tags, + comment: commentOrUndefined, + created_at, + created_by, + description, + entries: entriesArray, + id, + item_id, + list_id, + meta: metaOrUndefined, + name, + tags, + tie_breaker_id, + type: exceptionListItemType, + updated_at, + updated_by, + }) +); + +export type ExceptionListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts new file mode 100644 index 0000000000000..1940d94597dec --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + _tags, + created_at, + created_by, + description, + exceptionListType, + id, + list_id, + metaOrUndefined, + name, + tags, + tie_breaker_id, + updated_at, + updated_by, +} from '../common/schemas'; + +// TODO: Should we use a partial here to reflect that this can JSON serialize meta as non existent? +export const exceptionListSchema = t.exact( + t.type({ + _tags, + created_at, + created_by, + description, + id, + list_id, + meta: metaOrUndefined, + name, + tags, + tie_breaker_id, + type: exceptionListType, + updated_at, + updated_by, + }) +); + +export type ExceptionListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.ts new file mode 100644 index 0000000000000..a58bf433017e6 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.ts @@ -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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { page, per_page, total } from '../common/schemas'; + +import { exceptionListItemSchema } from './exception_list_item_schema'; + +export const foundExceptionListItemSchema = t.exact( + t.type({ + data: t.array(exceptionListItemSchema), + page, + per_page, + total, + }) +); + +export type FoundExceptionListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.ts new file mode 100644 index 0000000000000..a2ea09a3263ae --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.ts @@ -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 @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { page, per_page, total } from '../common/schemas'; + +import { exceptionListSchema } from './exception_list_schema'; + +export const foundExceptionListSchema = t.exact( + t.type({ + data: t.array(exceptionListSchema), + page, + per_page, + total, + }) +); + +export type FoundExceptionListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/index.ts b/x-pack/plugins/lists/common/schemas/response/index.ts index 3f11adf58d8d4..213685d1183bd 100644 --- a/x-pack/plugins/lists/common/schemas/response/index.ts +++ b/x-pack/plugins/lists/common/schemas/response/index.ts @@ -8,3 +8,7 @@ export * from './list_item_schema'; export * from './list_schema'; export * from './acknowledge_schema'; export * from './list_item_index_exist_schema'; +export * from './exception_list_schema'; +export * from './found_exception_list_item_schema'; +export * from './found_exception_list_schema'; +export * from './exception_list_item_schema'; diff --git a/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts new file mode 100644 index 0000000000000..a08bab65c881c --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { entriesArrayOrUndefined } from '../types'; +import { + _tags, + commentOrUndefined, + created_at, + created_by, + description, + exceptionListItemType, + exceptionListType, + itemIdOrUndefined, + list_id, + list_type, + metaOrUndefined, + name, + tags, + tie_breaker_id, + updated_by, +} from '../common/schemas'; + +export const exceptionListSoSchema = t.exact( + t.type({ + _tags, + comment: commentOrUndefined, + created_at, + created_by, + description, + entries: entriesArrayOrUndefined, + item_id: itemIdOrUndefined, + list_id, + list_type, + meta: metaOrUndefined, + name, + tags, + tie_breaker_id, + type: t.union([exceptionListType, exceptionListItemType]), + updated_by, + }) +); + +export type ExceptionListSoSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/saved_objects/index.ts b/x-pack/plugins/lists/common/schemas/saved_objects/index.ts new file mode 100644 index 0000000000000..8d42be2d145ca --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/saved_objects/index.ts @@ -0,0 +1,6 @@ +/* + * 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 './exceptions_list_so_schema'; diff --git a/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts new file mode 100644 index 0000000000000..43698665bb371 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +import { EntriesArray, entries } from './entries'; + +export type DefaultEntriesArrayC = t.Type; + +/** + * Types the DefaultEntriesArray as: + * - If null or undefined, then a default array of type entry will be set + */ +export const DefaultEntryArray: DefaultEntriesArrayC = new t.Type< + EntriesArray, + EntriesArray, + unknown +>( + 'DefaultEntryArray', + t.array(entries).is, + (input): Either => + input == null ? t.success([]) : t.array(entries).decode(input), + t.identity +); diff --git a/x-pack/plugins/lists/common/schemas/types/default_string_array.ts b/x-pack/plugins/lists/common/schemas/types/default_string_array.ts new file mode 100644 index 0000000000000..c6ebffa379903 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/default_string_array.ts @@ -0,0 +1,22 @@ +/* + * 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 { Either } from 'fp-ts/lib/Either'; + +export type DefaultStringArrayC = t.Type; + +/** + * Types the DefaultStringArray as: + * - If null or undefined, then a default array will be set + */ +export const DefaultStringArray: DefaultStringArrayC = new t.Type( + 'DefaultArray', + t.array(t.string).is, + (input): Either => + input == null ? t.success([]) : t.array(t.string).decode(input), + t.identity +); diff --git a/x-pack/plugins/lists/common/schemas/types/default_uuid.ts b/x-pack/plugins/lists/common/schemas/types/default_uuid.ts new file mode 100644 index 0000000000000..4fb4133d7353f --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/default_uuid.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 { Either } from 'fp-ts/lib/Either'; +import uuid from 'uuid'; + +import { NonEmptyString } from './non_empty_string'; + +export type DefaultUuidC = t.Type; + +/** + * Types the DefaultUuid as: + * - If null or undefined, then a default string uuid.v4() will be + * created otherwise it will be checked just against an empty string + */ +export const DefaultUuid: DefaultUuidC = new t.Type( + 'DefaultUuid', + t.string.is, + (input): Either => + input == null ? t.success(uuid.v4()) : NonEmptyString.decode(input), + t.identity +); diff --git a/x-pack/plugins/lists/common/schemas/types/entries.ts b/x-pack/plugins/lists/common/schemas/types/entries.ts new file mode 100644 index 0000000000000..750e04e2f39e5 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/entries.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 * as t from 'io-ts'; + +export const entries = t.exact( + t.type({ + field: t.string, + match: t.union([t.string, t.undefined]), + match_any: t.union([t.array(t.string), t.undefined]), + operator: t.string, // TODO: Use a key of with all possible values + }) +); + +export const entriesArray = t.array(entries); +export type EntriesArray = t.TypeOf; +export type Entries = t.TypeOf; +export const entriesArrayOrUndefined = t.union([entriesArray, t.undefined]); +export type EntriesArrayOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/index.ts b/x-pack/plugins/lists/common/schemas/types/index.ts new file mode 100644 index 0000000000000..674d7c40c2970 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './default_entries_array'; +export * from './default_string_array'; +export * from './default_uuid'; +export * from './entries'; +export * from './non_empty_string'; diff --git a/x-pack/plugins/lists/common/siem_common_deps.ts b/x-pack/plugins/lists/common/siem_common_deps.ts index c5242843dfbdd..eb4b393a36da3 100644 --- a/x-pack/plugins/lists/common/siem_common_deps.ts +++ b/x-pack/plugins/lists/common/siem_common_deps.ts @@ -4,7 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { getPaths, foldLeftRight } from '../../siem/server/utils/build_validation/__mocks__/utils'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { exactCheck } from '../../siem/server/utils/build_validation/exact_check'; +export { exactCheck } from '../../siem/common/exact_check'; +export { getPaths, foldLeftRight } from '../../siem/common/test_utils'; diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/plugins/lists/server/plugin.ts index 5facf981c098e..ed515757875be 100644 --- a/x-pack/plugins/lists/server/plugin.ts +++ b/x-pack/plugins/lists/server/plugin.ts @@ -13,7 +13,7 @@ import { SpacesServiceSetup } from '../../spaces/server'; import { ConfigType } from './config'; import { initRoutes } from './routes/init_routes'; -import { ListClient } from './services/lists/client'; +import { ListClient } from './services/lists/list_client'; import { ContextProvider, ContextProviderReturn, @@ -24,6 +24,8 @@ import { import { createConfig$ } from './create_config'; import { getSpaceId } from './get_space_id'; import { getUser } from './get_user'; +import { initSavedObjects } from './saved_objects'; +import { ExceptionListClient } from './services/exception_lists/exception_list_client'; export class ListPlugin implements Plugin, ListsPluginStart, PluginsSetup> { @@ -48,14 +50,22 @@ export class ListPlugin this.config = config; this.security = plugins.security; + initSavedObjects(core.savedObjects); + core.http.registerRouteHandlerContext('lists', this.createRouteHandlerContext()); const router = core.http.createRouter(); initRoutes(router); return { - getListClient: (apiCaller, spaceId, user): ListClient => { + getExceptionListClient: (savedObjectsClient, user): ExceptionListClient => { + return new ExceptionListClient({ + savedObjectsClient, + user, + }); + }, + getListClient: (callCluster, spaceId, user): ListClient => { return new ListClient({ - callCluster: apiCaller, + callCluster, config, spaceId, user, @@ -77,8 +87,9 @@ export class ListPlugin const { spaces, config, security } = this; const { core: { + savedObjects: { client: savedObjectsClient }, elasticsearch: { - dataClient: { callAsCurrentUser }, + dataClient: { callAsCurrentUser: callCluster }, }, }, } = context; @@ -88,9 +99,14 @@ export class ListPlugin const spaceId = getSpaceId({ request, spaces }); const user = getUser({ request, security }); return { + getExceptionListClient: (): ExceptionListClient => + new ExceptionListClient({ + savedObjectsClient, + user, + }), getListClient: (): ListClient => new ListClient({ - callCluster: callAsCurrentUser, + callCluster, config, spaceId, user, diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts new file mode 100644 index 0000000000000..ddcae137a961a --- /dev/null +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -0,0 +1,109 @@ +/* + * 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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + CreateExceptionListItemSchemaDecoded, + createExceptionListItemSchema, + exceptionListItemSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from './utils/get_exception_list_client'; + +export const createExceptionListItemRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + body: buildRouteValidation< + typeof createExceptionListItemSchema, + CreateExceptionListItemSchemaDecoded + >(createExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { + name, + _tags, + tags, + meta, + comment, + description, + entries, + item_id: itemId, + list_id: listId, + type, + } = request.body; + const exceptionLists = getExceptionListClient(context); + const exceptionList = await exceptionLists.getExceptionList({ + id: undefined, + listId, + // TODO: Expose the name space type + namespaceType: 'single', + }); + if (exceptionList == null) { + return siemResponse.error({ + body: `list id: "${listId}" does not exist`, + statusCode: 404, + }); + } else { + const exceptionListItem = await exceptionLists.getExceptionListItem({ + id: undefined, + itemId, + // TODO: Expose the name space type + namespaceType: 'single', + }); + if (exceptionListItem != null) { + return siemResponse.error({ + body: `exception list item id: "${itemId}" already exists`, + statusCode: 409, + }); + } else { + const createdList = await exceptionLists.createExceptionListItem({ + _tags, + comment, + description, + entries, + itemId, + listId, + meta, + name, + // TODO: Expose the name space type + namespaceType: 'single', + tags, + type, + }); + const [validated, errors] = validate(createdList, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts new file mode 100644 index 0000000000000..c8a1b080c16f6 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -0,0 +1,82 @@ +/* + * 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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + CreateExceptionListSchemaDecoded, + createExceptionListSchema, + exceptionListSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from './utils/get_exception_list_client'; + +export const createExceptionListRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + body: buildRouteValidation< + typeof createExceptionListSchema, + CreateExceptionListSchemaDecoded + >(createExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { name, _tags, tags, meta, description, list_id: listId, type } = request.body; + const exceptionLists = getExceptionListClient(context); + const exceptionList = await exceptionLists.getExceptionList({ + id: undefined, + listId, + // TODO: Expose the name space type + namespaceType: 'single', + }); + if (exceptionList != null) { + return siemResponse.error({ + body: `exception list id: "${listId}" already exists`, + statusCode: 409, + }); + } else { + const createdList = await exceptionLists.createExceptionList({ + _tags, + description, + listId, + meta, + name, + // TODO: Expose the name space type + namespaceType: 'single', + tags, + type, + }); + const [validated, errors] = validate(createdList, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts new file mode 100644 index 0000000000000..e10ffab5359b0 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts @@ -0,0 +1,70 @@ +/* + * 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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { deleteExceptionListItemSchema, exceptionListItemSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; + +export const deleteExceptionListItemRoute = (router: IRouter): void => { + router.delete( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + query: buildRouteValidation(deleteExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { item_id: itemId, id } = request.query; + if (itemId == null && id == null) { + return siemResponse.error({ + body: 'Either "item_id" or "id" needs to be defined in the request', + statusCode: 400, + }); + } else { + const deleted = await exceptionLists.deleteExceptionListItem({ + id, + itemId, + namespaceType: 'single', // TODO: Bubble this up + }); + if (deleted == null) { + return siemResponse.error({ + body: getErrorMessageExceptionListItem({ id, itemId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(deleted, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts new file mode 100644 index 0000000000000..ef30ab6ab64c5 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_route.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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { deleteExceptionListSchema, exceptionListSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionList, getExceptionListClient } from './utils'; + +export const deleteExceptionListRoute = (router: IRouter): void => { + router.delete( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + query: buildRouteValidation(deleteExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { list_id: listId, id } = request.query; + if (listId == null && id == null) { + return siemResponse.error({ + body: 'Either "list_id" or "id" needs to be defined in the request', + statusCode: 400, + }); + } else { + // TODO: At the moment this will delete the list but we need to delete all the list items before deleting the list + const deleted = await exceptionLists.deleteExceptionList({ + id, + listId, + namespaceType: 'single', + }); + if (deleted == null) { + return siemResponse.error({ + body: getErrorMessageExceptionList({ id, listId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(deleted, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts index 51b4eb9f02cc2..82dfe8a4f29d0 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts @@ -73,7 +73,7 @@ export const deleteListItemRoute = (router: IRouter): void => { } } else { return siemResponse.error({ - body: `Either "list_id" or "id" needs to be defined in the request`, + body: 'Either "list_id" or "id" needs to be defined in the request', statusCode: 400, }); } diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts new file mode 100644 index 0000000000000..3b5503ffb9833 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts @@ -0,0 +1,73 @@ +/* + * 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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { findExceptionListItemSchema, foundExceptionListItemSchema } from '../../common/schemas'; + +import { getExceptionListClient } from './utils'; + +export const findExceptionListItemRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: `${EXCEPTION_LIST_ITEM_URL}/_find`, + validate: { + query: buildRouteValidation(findExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { + filter, + list_id: listId, + page, + per_page: perPage, + sort_field: sortField, + sort_order: sortOrder, + } = request.query; + const exceptionListItems = await exceptionLists.findExceptionListItem({ + filter, + listId, + namespaceType: 'single', // TODO: Bubble this up + page, + perPage, + sortField, + sortOrder, + }); + if (exceptionListItems == null) { + return siemResponse.error({ + body: `list id: "${listId}" does not exist`, + statusCode: 404, + }); + } + const [validated, errors] = validate(exceptionListItems, foundExceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts new file mode 100644 index 0000000000000..41c0c0760e03b --- /dev/null +++ b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts @@ -0,0 +1,65 @@ +/* + * 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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { findExceptionListSchema, foundExceptionListSchema } from '../../common/schemas'; + +import { getExceptionListClient } from './utils'; + +export const findExceptionListRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: `${EXCEPTION_LIST_URL}/_find`, + validate: { + query: buildRouteValidation(findExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { + filter, + page, + per_page: perPage, + sort_field: sortField, + sort_order: sortOrder, + } = request.query; + const exceptionListItems = await exceptionLists.findExceptionList({ + filter, + namespaceType: 'single', // TODO: Bubble this up + page, + perPage, + sortField, + sortOrder, + }); + const [validated, errors] = validate(exceptionListItems, foundExceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/plugins/lists/server/routes/index.ts index 4951cddc56939..97f497bca7183 100644 --- a/x-pack/plugins/lists/server/routes/index.ts +++ b/x-pack/plugins/lists/server/routes/index.ts @@ -4,18 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ +export * from './create_exception_list_item_route'; +export * from './create_exception_list_route'; export * from './create_list_index_route'; export * from './create_list_item_route'; export * from './create_list_route'; +export * from './delete_exception_list_route'; +export * from './delete_exception_list_item_route'; export * from './delete_list_index_route'; export * from './delete_list_item_route'; export * from './delete_list_route'; export * from './export_list_item_route'; +export * from './find_exception_list_item_route'; +export * from './find_exception_list_route'; export * from './import_list_item_route'; export * from './init_routes'; export * from './patch_list_item_route'; export * from './patch_list_route'; +export * from './read_exception_list_item_route'; +export * from './read_exception_list_route'; export * from './read_list_index_route'; export * from './read_list_item_route'; export * from './read_list_route'; +export * from './update_exception_list_item_route'; +export * from './update_exception_list_route'; +export * from './update_list_item_route'; +export * from './update_list_route'; export * from './utils'; diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts index 924dd086ee708..16f96d99505d8 100644 --- a/x-pack/plugins/lists/server/routes/init_routes.ts +++ b/x-pack/plugins/lists/server/routes/init_routes.ts @@ -6,23 +6,32 @@ import { IRouter } from 'kibana/server'; -import { updateListRoute } from './update_list_route'; -import { updateListItemRoute } from './update_list_item_route'; - import { + createExceptionListItemRoute, + createExceptionListRoute, createListIndexRoute, createListItemRoute, createListRoute, + deleteExceptionListItemRoute, + deleteExceptionListRoute, deleteListIndexRoute, deleteListItemRoute, deleteListRoute, exportListItemRoute, + findExceptionListItemRoute, + findExceptionListRoute, importListItemRoute, patchListItemRoute, patchListRoute, + readExceptionListItemRoute, + readExceptionListRoute, readListIndexRoute, readListItemRoute, readListRoute, + updateExceptionListItemRoute, + updateExceptionListRoute, + updateListItemRoute, + updateListRoute, } from '.'; export const initRoutes = (router: IRouter): void => { @@ -33,7 +42,7 @@ export const initRoutes = (router: IRouter): void => { deleteListRoute(router); patchListRoute(router); - // lists items + // list items createListItemRoute(router); readListItemRoute(router); updateListItemRoute(router); @@ -46,4 +55,18 @@ export const initRoutes = (router: IRouter): void => { createListIndexRoute(router); readListIndexRoute(router); deleteListIndexRoute(router); + + // exception lists + createExceptionListRoute(router); + readExceptionListRoute(router); + updateExceptionListRoute(router); + deleteExceptionListRoute(router); + findExceptionListRoute(router); + + // exception list items + createExceptionListItemRoute(router); + readExceptionListItemRoute(router); + updateExceptionListItemRoute(router); + deleteExceptionListItemRoute(router); + findExceptionListItemRoute(router); }; diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts new file mode 100644 index 0000000000000..77d37373549c7 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts @@ -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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { exceptionListItemSchema, readExceptionListItemSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; + +export const readExceptionListItemRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + query: buildRouteValidation(readExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, item_id: itemId } = request.query; + const exceptionLists = getExceptionListClient(context); + if (id != null || itemId != null) { + const exceptionListItem = await exceptionLists.getExceptionListItem({ + id, + itemId, + // TODO: Bubble this up + namespaceType: 'single', + }); + if (exceptionListItem == null) { + return siemResponse.error({ + body: getErrorMessageExceptionListItem({ id, itemId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(exceptionListItem, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } else { + return siemResponse.error({ body: 'id or item_id required', statusCode: 400 }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts new file mode 100644 index 0000000000000..1668124acdfce --- /dev/null +++ b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts @@ -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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { exceptionListSchema, readExceptionListSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionList, getExceptionListClient } from './utils'; + +export const readExceptionListRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + query: buildRouteValidation(readExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, list_id: listId } = request.query; + const exceptionLists = getExceptionListClient(context); + if (id != null || listId != null) { + const exceptionList = await exceptionLists.getExceptionList({ + id, + listId, + // TODO: Bubble this up + namespaceType: 'single', + }); + if (exceptionList == null) { + return siemResponse.error({ + body: getErrorMessageExceptionList({ id, listId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(exceptionList, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } else { + return siemResponse.error({ body: 'id or list_id required', statusCode: 400 }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/read_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_list_item_route.ts index 0a60cba786f04..10c7f781f554c 100644 --- a/x-pack/plugins/lists/server/routes/read_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_item_route.ts @@ -77,7 +77,7 @@ export const readListItemRoute = (router: IRouter): void => { } } else { return siemResponse.error({ - body: `Either "list_id" or "id" needs to be defined in the request`, + body: 'Either "list_id" or "id" needs to be defined in the request', statusCode: 400, }); } diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts new file mode 100644 index 0000000000000..478225ee35eb8 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -0,0 +1,89 @@ +/* + * 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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + UpdateExceptionListItemSchemaDecoded, + exceptionListItemSchema, + updateExceptionListItemSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from '.'; + +export const updateExceptionListItemRoute = (router: IRouter): void => { + router.put( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + body: buildRouteValidation< + typeof updateExceptionListItemSchema, + UpdateExceptionListItemSchemaDecoded + >(updateExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { + description, + id, + name, + meta, + type, + _tags, + comment, + entries, + item_id: itemId, + tags, + } = request.body; + const exceptionLists = getExceptionListClient(context); + const exceptionListItem = await exceptionLists.updateExceptionListItem({ + _tags, + comment, + description, + entries, + id, + itemId, + meta, + name, + namespaceType: 'single', // TODO: Bubble this up + tags, + type, + }); + if (exceptionListItem == null) { + return siemResponse.error({ + body: `list item id: "${id}" not found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(exceptionListItem, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts new file mode 100644 index 0000000000000..a112c7422b952 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -0,0 +1,83 @@ +/* + * 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 { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + UpdateExceptionListSchemaDecoded, + exceptionListSchema, + updateExceptionListSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from './utils'; + +export const updateExceptionListRoute = (router: IRouter): void => { + router.put( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + body: buildRouteValidation< + typeof updateExceptionListSchema, + UpdateExceptionListSchemaDecoded + >(updateExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { _tags, tags, name, description, id, list_id: listId, meta, type } = request.body; + const exceptionLists = getExceptionListClient(context); + if (id == null && listId == null) { + return siemResponse.error({ + body: `either id or list_id need to be defined`, + statusCode: 404, + }); + } else { + const list = await exceptionLists.updateExceptionList({ + _tags, + description, + id, + listId, + meta, + name, + namespaceType: 'single', // TODO: Bubble this up + tags, + type, + }); + if (list == null) { + return siemResponse.error({ + body: `exception list id: "${id}" found found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(list, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts new file mode 100644 index 0000000000000..665a7540184a0 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.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. + */ + +export const getErrorMessageExceptionList = ({ + id, + listId, +}: { + id: string | undefined; + listId: string | undefined; +}): string => { + if (id != null) { + return `Exception list id: "${id}" does not exist`; + } else if (listId != null) { + return `Exception list list_id: "${listId}" does not exist`; + } else { + return 'Exception list does not exist'; + } +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts new file mode 100644 index 0000000000000..8e6730ef3db5c --- /dev/null +++ b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.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. + */ + +export const getErrorMessageExceptionListItem = ({ + id, + itemId, +}: { + id: string | undefined; + itemId: string | undefined; +}): string => { + if (id != null) { + return `Exception list item id: "${id}" does not exist`; + } else if (itemId != null) { + return `Exception list item list_id: "${itemId}" does not exist`; + } else { + return 'Exception list item does not exist'; + } +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts new file mode 100644 index 0000000000000..ba01ca617fb8b --- /dev/null +++ b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RequestHandlerContext } from 'kibana/server'; + +import { ErrorWithStatusCode } from '../../error_with_status_code'; +import { ExceptionListClient } from '../../services/exception_lists/exception_list_client'; + +export const getExceptionListClient = (context: RequestHandlerContext): ExceptionListClient => { + const exceptionLists = context.lists?.getExceptionListClient(); + if (exceptionLists == null) { + throw new ErrorWithStatusCode('Exception lists is not found as a plugin', 404); + } else { + return exceptionLists; + } +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts index a16163ec0fa3a..6ad69fd994bfd 100644 --- a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts +++ b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts @@ -6,7 +6,7 @@ import { RequestHandlerContext } from 'kibana/server'; -import { ListClient } from '../../services/lists/client'; +import { ListClient } from '../../services/lists/list_client'; import { ErrorWithStatusCode } from '../../error_with_status_code'; export const getListClient = (context: RequestHandlerContext): ListClient => { diff --git a/x-pack/plugins/lists/server/routes/utils/index.ts b/x-pack/plugins/lists/server/routes/utils/index.ts index a601bdfc003c5..90214872b68e3 100644 --- a/x-pack/plugins/lists/server/routes/utils/index.ts +++ b/x-pack/plugins/lists/server/routes/utils/index.ts @@ -3,4 +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. */ +export * from './get_error_message_exception_list_item'; +export * from './get_error_message_exception_list'; export * from './get_list_client'; +export * from './get_exception_list_client'; diff --git a/x-pack/plugins/lists/server/saved_objects/exception_list.ts b/x-pack/plugins/lists/server/saved_objects/exception_list.ts new file mode 100644 index 0000000000000..9e1a708e0c83b --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/exception_list.ts @@ -0,0 +1,115 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; + +export const exceptionListSavedObjectType = 'exception-list'; +export const exceptionListAgnosticSavedObjectType = 'exception-list-agnostic'; +export type SavedObjectType = 'exception-list' | 'exception-list-agnostic'; + +/** + * This is a super set of exception list and exception list items. The switch + * to determine if you are using an exception list vs. an exception list item + * is "list_type". If "list_type" is "list" then it is an exception list. If + * "list_type" is "item" then the type is an item. + */ +export const commonMapping: SavedObjectsType['mappings'] = { + properties: { + _tags: { + type: 'keyword', + }, + created_at: { + type: 'keyword', + }, + created_by: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + list_id: { + type: 'keyword', + }, + list_type: { + type: 'keyword', + }, + meta: { + type: 'keyword', + }, + name: { + type: 'keyword', + }, + tags: { + type: 'keyword', + }, + tie_breaker_id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + updated_by: { + type: 'keyword', + }, + }, +}; + +export const exceptionListMapping: SavedObjectsType['mappings'] = { + properties: { + // There is nothing that is not also used within exceptionListItemMapping + // at this time but if there is it should go here + }, +}; + +export const exceptionListItemMapping: SavedObjectsType['mappings'] = { + properties: { + comment: { + // TODO: investigate what the deep mapping structure of this really is + type: 'keyword', + }, + entries: { + properties: { + field: { + type: 'keyword', + }, + match: { + type: 'keyword', + }, + match_any: { + type: 'keyword', + }, + operator: { + type: 'keyword', + }, + }, + }, + item_id: { + type: 'keyword', + }, + }, +}; + +const combinedMappings: SavedObjectsType['mappings'] = { + properties: { + ...commonMapping.properties, + ...exceptionListMapping.properties, + ...exceptionListItemMapping.properties, + }, +}; + +export const exceptionListType: SavedObjectsType = { + hidden: false, + mappings: combinedMappings, + name: exceptionListSavedObjectType, + namespaceType: 'single', +}; + +export const exceptionListAgnosticType: SavedObjectsType = { + hidden: false, + mappings: combinedMappings, + name: exceptionListAgnosticSavedObjectType, + namespaceType: 'agnostic', +}; diff --git a/x-pack/plugins/lists/server/saved_objects/index.ts b/x-pack/plugins/lists/server/saved_objects/index.ts new file mode 100644 index 0000000000000..b5077ae100cde --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './exception_list'; +export * from './init_saved_objects'; diff --git a/x-pack/plugins/lists/server/saved_objects/init_saved_objects.ts b/x-pack/plugins/lists/server/saved_objects/init_saved_objects.ts new file mode 100644 index 0000000000000..aab5ffd0e57fe --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/init_saved_objects.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 { CoreSetup } from 'kibana/server'; + +import { exceptionListAgnosticType, exceptionListType } from './exception_list'; + +export const initSavedObjects = (savedObjects: CoreSetup['savedObjects']): void => { + savedObjects.registerType(exceptionListAgnosticType); + savedObjects.registerType(exceptionListType); +}; diff --git a/x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh b/x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh new file mode 100755 index 0000000000000..bb431800c56c3 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh @@ -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. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_all_alerts.sh +# https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html +curl -s -k \ + -H "Content-Type: application/json" \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \ + --data '{ + "query": { + "exists": { "field": "exception-list" } + } + }' \ + | jq . + +# Deletes all the agnostic namespace version +curl -s -k \ + -H "Content-Type: application/json" \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \ + --data '{ + "query": { + "exists": { "field": "exception-list-agnostic" } + } + }' \ + | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list.sh new file mode 100755 index 0000000000000..fe2ca501b4416 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list.sh ${list_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh new file mode 100755 index 0000000000000..a87881b385328 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list_by_id.sh ${list_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh new file mode 100755 index 0000000000000..7e09452a23e11 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list_item.sh ${item_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh new file mode 100755 index 0000000000000..bbfbc3135ddb8 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list_item_by_id.sh ${list_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_list.sh b/x-pack/plugins/lists/server/scripts/delete_list.sh index 9934ce61c7107..ce9fdd6aa21d4 100755 --- a/x-pack/plugins/lists/server/scripts/delete_list.sh +++ b/x-pack/plugins/lists/server/scripts/delete_list.sh @@ -9,7 +9,7 @@ set -e ./check_env_variables.sh -# Example: ./delete_list_by_list_id.sh ${list_id} +# Example: ./delete_list.sh ${list_id} curl -s -k \ -H 'kbn-xsrf: 123' \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json new file mode 100644 index 0000000000000..520bc4ddf1e09 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json @@ -0,0 +1,8 @@ +{ + "list_id": "endpoint_list", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "endpoint", + "description": "This is a sample endpoint type exception", + "name": "Sample Endpoint Exception List" +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json new file mode 100644 index 0000000000000..8c039bf6788ae --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json @@ -0,0 +1,5 @@ +{ + "description": "This is a sample exception list with auto created list_id since none was provided", + "name": "Sample Exception List without a whole lot going on", + "type": "endpoint" +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json new file mode 100644 index 0000000000000..2b97f37a7fa6b --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json @@ -0,0 +1,21 @@ +{ + "list_id": "endpoint_list", + "item_id": "endpoint_list_item", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "simple", + "description": "This is a sample endpoint type exception", + "name": "Sample Endpoint Exception List", + "entries": [ + { + "field": "actingProcess.file.signer", + "operator": "included", + "match": "Elastic, N.V." + }, + { + "field": "event.category", + "operator": "included", + "match_any": ["process", "malware"] + } + ] +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json new file mode 100644 index 0000000000000..d68a26eb8ffe2 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json @@ -0,0 +1,20 @@ +{ + "list_id": "endpoint_list", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "simple", + "description": "This is a sample endpoint type exception that has no item_id so it creates a new id each time", + "name": "Sample Endpoint Exception List", + "entries": [ + { + "field": "actingProcess.file.signer", + "operator": "included", + "match": "Elastic, N.V." + }, + { + "field": "event.category", + "operator": "included", + "match_any": ["process", "malware"] + } + ] +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json new file mode 100644 index 0000000000000..a7fbe1ea48c02 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json @@ -0,0 +1,8 @@ +{ + "list_id": "endpoint_list", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "endpoint", + "description": "Different description", + "name": "Sample Endpoint Exception List" +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json new file mode 100644 index 0000000000000..a53079318edfa --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json @@ -0,0 +1,15 @@ +{ + "item_id": "endpoint_list_item", + "_tags": ["endpoint", "process", "malware", "os:windows"], + "tags": ["user added string for a tag", "malware"], + "type": "simple", + "description": "This is a sample change here this list", + "name": "Sample Endpoint Exception List update change", + "entries": [ + { + "field": "event.category", + "operator": "included", + "match_any": ["process", "malware"] + } + ] +} diff --git a/x-pack/plugins/lists/server/scripts/find_exception_list_items.sh b/x-pack/plugins/lists/server/scripts/find_exception_list_items.sh new file mode 100755 index 0000000000000..85c5b0e518fab --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/find_exception_list_items.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +LIST_ID=${1:-endpoint_list} +# Example: ./find_exception_list_items.sh {list-id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items/_find?list_id=${LIST_ID} | jq . diff --git a/x-pack/plugins/lists/server/scripts/find_exception_lists.sh b/x-pack/plugins/lists/server/scripts/find_exception_lists.sh new file mode 100755 index 0000000000000..a1ee184b3e5bb --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/find_exception_lists.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./find_exception_lists.sh {list-id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/_find | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list.sh b/x-pack/plugins/lists/server/scripts/get_exception_list.sh new file mode 100755 index 0000000000000..34e6de2576879 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh b/x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh new file mode 100755 index 0000000000000..0420a1f702328 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list_by_id.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/get_exception_list_item.sh new file mode 100755 index 0000000000000..ac8337aab8368 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list_item.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list_item.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh b/x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh new file mode 100755 index 0000000000000..575a529c69906 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list_item_by_id.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/hard_reset.sh b/x-pack/plugins/lists/server/scripts/hard_reset.sh index 861928866369b..e00c3a9ace34f 100755 --- a/x-pack/plugins/lists/server/scripts/hard_reset.sh +++ b/x-pack/plugins/lists/server/scripts/hard_reset.sh @@ -12,3 +12,6 @@ set -e # re-create the list and list item indexes ./delete_list_index.sh ./post_list_index.sh + +# re-create the exception list and exception list items +./delete_all_exception_lists.sh diff --git a/x-pack/plugins/lists/server/scripts/post_exception_list.sh b/x-pack/plugins/lists/server/scripts/post_exception_list.sh new file mode 100755 index 0000000000000..84a775ffcf7f1 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/post_exception_list.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/new/exception_list.json}) + +# Example: ./post_exception_list.sh +# Example: ./post_exception_list.sh ./exception_lists/new/exception_list.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/exception_lists \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/post_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/post_exception_list_item.sh new file mode 100755 index 0000000000000..6cee54b1a6148 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/post_exception_list_item.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/new/exception_list_item.json}) + +# Example: ./post_exception_list_item.sh +# Example: ./post_exception_list_item.sh ./exception_lists/new/exception_list_item.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/update_exception_list.sh b/x-pack/plugins/lists/server/scripts/update_exception_list.sh new file mode 100755 index 0000000000000..d7523a0804a89 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/update_exception_list.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/updates/simple_update.json}) + +# Example: ./update_exception_list.sh +# Example: ./update_exception_list.sh ./exception_lists/updates/simple_update.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PUT ${KIBANA_URL}${SPACE_URL}/api/exception_lists \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/update_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/update_exception_list_item.sh new file mode 100755 index 0000000000000..029bfcdabee3e --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/update_exception_list_item.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/updates/simple_update_item.json}) + +# Example: ./update_exception_list_item.sh +# Example: ./update_exception_list_item.sh ./exception_lists/updates/simple_update_item.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PUT ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts new file mode 100644 index 0000000000000..7ba832e72bb8e --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; +import uuid from 'uuid'; + +import { + Description, + ExceptionListSchema, + ExceptionListSoSchema, + ExceptionListType, + ListId, + MetaOrUndefined, + Name, + Tags, + _Tags, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils'; +import { NamespaceType } from './types'; + +interface CreateExceptionListOptions { + _tags: _Tags; + listId: ListId; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + name: Name; + description: Description; + meta: MetaOrUndefined; + user: string; + tags: Tags; + tieBreaker?: string; + type: ExceptionListType; +} + +export const createExceptionList = async ({ + _tags, + listId, + savedObjectsClient, + namespaceType, + name, + description, + meta, + user, + tags, + tieBreaker, + type, +}: CreateExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const dateNow = new Date().toISOString(); + const savedObject = await savedObjectsClient.create(savedObjectType, { + _tags, + comment: undefined, + created_at: dateNow, + created_by: user, + description, + entries: undefined, + item_id: undefined, + list_id: listId, + list_type: 'list', + meta, + name, + tags, + tie_breaker_id: tieBreaker ?? uuid.v4(), + type, + updated_by: user, + }); + return transformSavedObjectToExceptionList({ savedObject }); +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts new file mode 100644 index 0000000000000..4a6dc1da97854 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.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 { SavedObjectsClientContract } from 'kibana/server'; +import uuid from 'uuid'; + +import { + CommentOrUndefined, + Description, + EntriesArray, + ExceptionListItemSchema, + ExceptionListSoSchema, + ExceptionListType, + ItemId, + ListId, + MetaOrUndefined, + Name, + Tags, + _Tags, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionListItem } from './utils'; +import { NamespaceType } from './types'; + +interface CreateExceptionListItemOptions { + _tags: _Tags; + comment: CommentOrUndefined; + listId: ListId; + itemId: ItemId; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + name: Name; + description: Description; + entries: EntriesArray; + meta: MetaOrUndefined; + user: string; + tags: Tags; + tieBreaker?: string; + type: ExceptionListType; +} + +export const createExceptionListItem = async ({ + _tags, + comment, + entries, + itemId, + listId, + savedObjectsClient, + namespaceType, + name, + description, + meta, + user, + tags, + tieBreaker, + type, +}: CreateExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const dateNow = new Date().toISOString(); + const savedObject = await savedObjectsClient.create(savedObjectType, { + _tags, + comment, + created_at: dateNow, + created_by: user, + description, + entries, + item_id: itemId, + list_id: listId, + list_type: 'item', + meta, + name, + tags, + tie_breaker_id: tieBreaker ?? uuid.v4(), + type, + updated_by: user, + }); + return transformSavedObjectToExceptionListItem({ savedObject }); +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts new file mode 100644 index 0000000000000..6904438c8d275 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts @@ -0,0 +1,42 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/server'; + +import { ExceptionListSchema, IdOrUndefined, ListIdOrUndefined } from '../../../common/schemas'; + +import { getSavedObjectType } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionList } from './get_exception_list'; +import { deleteExceptionListItemByList } from './delete_exception_list_items_by_list'; + +interface DeleteExceptionListOptions { + listId: ListIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; +} + +export const deleteExceptionList = async ({ + listId, + id, + namespaceType, + savedObjectsClient, +}: DeleteExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionList = await getExceptionList({ id, listId, namespaceType, savedObjectsClient }); + if (exceptionList == null) { + return null; + } else { + await deleteExceptionListItemByList({ + listId: exceptionList.list_id, + namespaceType, + savedObjectsClient, + }); + await savedObjectsClient.delete(savedObjectType, exceptionList.id); + return exceptionList; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts new file mode 100644 index 0000000000000..3b2d991281cd6 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.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 { SavedObjectsClientContract } from 'kibana/server'; + +import { ExceptionListItemSchema, IdOrUndefined, ItemIdOrUndefined } from '../../../common/schemas'; + +import { getSavedObjectType } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionListItem } from './get_exception_list_item'; + +interface DeleteExceptionListItemOptions { + itemId: ItemIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; +} + +export const deleteExceptionListItem = async ({ + itemId, + id, + namespaceType, + savedObjectsClient, +}: DeleteExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionListItem = await getExceptionListItem({ + id, + itemId, + namespaceType, + savedObjectsClient, + }); + if (exceptionListItem == null) { + return null; + } else { + await savedObjectsClient.delete(savedObjectType, exceptionListItem.id); + return exceptionListItem; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts new file mode 100644 index 0000000000000..8031f085c1635 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts @@ -0,0 +1,89 @@ +/* + * 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 { SavedObjectsClientContract } from '../../../../../../src/core/server/'; +import { ListId } from '../../../common/schemas'; + +import { findExceptionListItem } from './find_exception_list_item'; +import { NamespaceType } from './types'; +import { getSavedObjectType } from './utils'; + +const PER_PAGE = 100; + +interface DeleteExceptionListItemByListOptions { + listId: ListId; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; +} + +export const deleteExceptionListItemByList = async ({ + listId, + savedObjectsClient, + namespaceType, +}: DeleteExceptionListItemByListOptions): Promise => { + const ids = await getExceptionListItemIds({ listId, namespaceType, savedObjectsClient }); + await deleteFoundExceptionListItems({ ids, namespaceType, savedObjectsClient }); +}; + +export const getExceptionListItemIds = async ({ + listId, + savedObjectsClient, + namespaceType, +}: DeleteExceptionListItemByListOptions): Promise => { + let page = 1; + let ids: string[] = []; + let foundExceptionListItems = await findExceptionListItem({ + filter: undefined, + listId, + namespaceType, + page, + perPage: PER_PAGE, + savedObjectsClient, + sortField: 'tie_breaker_id', + sortOrder: 'desc', + }); + while (foundExceptionListItems != null && foundExceptionListItems.data.length > 0) { + ids = [...ids, ...foundExceptionListItems.data.map(exceptionListItem => exceptionListItem.id)]; + page += 1; + foundExceptionListItems = await findExceptionListItem({ + filter: undefined, + listId, + namespaceType, + page, + perPage: PER_PAGE, + savedObjectsClient, + sortField: 'tie_breaker_id', + sortOrder: 'desc', + }); + } + return ids; +}; + +/** + * NOTE: This is slow and terrible as we are deleting everything one at a time. + * TODO: Replace this with a bulk call or a delete by query would be more useful + */ +export const deleteFoundExceptionListItems = async ({ + ids, + savedObjectsClient, + namespaceType, +}: { + ids: string[]; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +}): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + ids.forEach(async id => { + try { + await savedObjectsClient.delete(savedObjectType, id); + } catch (err) { + // This can happen from race conditions or networking issues so deleting the id's + // like this is considered "best effort" and it is possible to get dangling pieces + // of data sitting around in which case the user has to manually clean up the data + // I am very hopeful this does not happen often or at all. + } + }); +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts new file mode 100644 index 0000000000000..6e71ed1b3e59d --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -0,0 +1,250 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/server'; + +import { + ExceptionListSchema, + FoundExceptionListItemSchema, + FoundExceptionListSchema, +} from '../../../common/schemas'; + +import { + ConstructorOptions, + CreateExceptionListItemOptions, + CreateExceptionListOptions, + DeleteExceptionListItemOptions, + DeleteExceptionListOptions, + FindExceptionListItemOptions, + FindExceptionListOptions, + GetExceptionListItemOptions, + GetExceptionListOptions, + UpdateExceptionListItemOptions, + UpdateExceptionListOptions, +} from './exception_list_client_types'; +import { getExceptionList } from './get_exception_list'; +import { createExceptionList } from './create_exception_list'; +import { getExceptionListItem } from './get_exception_list_item'; +import { createExceptionListItem } from './create_exception_list_item'; +import { updateExceptionList } from './update_exception_list'; +import { updateExceptionListItem } from './update_exception_list_item'; +import { deleteExceptionList } from './delete_exception_list'; +import { deleteExceptionListItem } from './delete_exception_list_item'; +import { findExceptionListItem } from './find_exception_list_item'; +import { findExceptionList } from './find_exception_list'; + +export class ExceptionListClient { + private readonly user: string; + + private readonly savedObjectsClient: SavedObjectsClientContract; + + constructor({ user, savedObjectsClient }: ConstructorOptions) { + this.user = user; + this.savedObjectsClient = savedObjectsClient; + } + + public getExceptionList = async ({ + listId, + id, + namespaceType, + }: GetExceptionListOptions): Promise => { + const { savedObjectsClient } = this; + return getExceptionList({ id, listId, namespaceType, savedObjectsClient }); + }; + + public getExceptionListItem = async ({ + itemId, + id, + namespaceType, + }: GetExceptionListItemOptions): Promise => { + const { savedObjectsClient } = this; + return getExceptionListItem({ id, itemId, namespaceType, savedObjectsClient }); + }; + + public createExceptionList = async ({ + _tags, + description, + listId, + meta, + name, + namespaceType, + tags, + type, + }: CreateExceptionListOptions): Promise => { + const { savedObjectsClient, user } = this; + return createExceptionList({ + _tags, + description, + listId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public updateExceptionList = async ({ + _tags, + id, + description, + listId, + meta, + name, + namespaceType, + tags, + type, + }: UpdateExceptionListOptions): Promise => { + const { savedObjectsClient, user } = this; + return updateExceptionList({ + _tags, + description, + id, + listId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public deleteExceptionList = async ({ + id, + listId, + namespaceType, + }: DeleteExceptionListOptions): Promise => { + const { savedObjectsClient } = this; + return deleteExceptionList({ + id, + listId, + namespaceType, + savedObjectsClient, + }); + }; + + public createExceptionListItem = async ({ + _tags, + comment, + description, + entries, + itemId, + listId, + meta, + name, + namespaceType, + tags, + type, + }: CreateExceptionListItemOptions): Promise => { + const { savedObjectsClient, user } = this; + return createExceptionListItem({ + _tags, + comment, + description, + entries, + itemId, + listId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public updateExceptionListItem = async ({ + _tags, + comment, + description, + entries, + id, + itemId, + meta, + name, + namespaceType, + tags, + type, + }: UpdateExceptionListItemOptions): Promise => { + const { savedObjectsClient, user } = this; + return updateExceptionListItem({ + _tags, + comment, + description, + entries, + id, + itemId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public deleteExceptionListItem = async ({ + id, + itemId, + namespaceType, + }: DeleteExceptionListItemOptions): Promise => { + const { savedObjectsClient } = this; + return deleteExceptionListItem({ + id, + itemId, + namespaceType, + savedObjectsClient, + }); + }; + + public findExceptionListItem = async ({ + listId, + filter, + perPage, + page, + sortField, + sortOrder, + namespaceType, + }: FindExceptionListItemOptions): Promise => { + const { savedObjectsClient } = this; + return findExceptionListItem({ + filter, + listId, + namespaceType, + page, + perPage, + savedObjectsClient, + sortField, + sortOrder, + }); + }; + + public findExceptionList = async ({ + filter, + perPage, + page, + sortField, + sortOrder, + namespaceType, + }: FindExceptionListOptions): Promise => { + const { savedObjectsClient } = this; + return findExceptionList({ + filter, + namespaceType, + page, + perPage, + savedObjectsClient, + sortField, + sortOrder, + }); + }; +} diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts new file mode 100644 index 0000000000000..cecd6bf3397a7 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -0,0 +1,130 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/server'; + +import { + CommentOrUndefined, + Description, + DescriptionOrUndefined, + EntriesArray, + EntriesArrayOrUndefined, + ExceptionListType, + ExceptionListTypeOrUndefined, + IdOrUndefined, + ItemId, + ItemIdOrUndefined, + ListId, + ListIdOrUndefined, + MetaOrUndefined, + Name, + NameOrUndefined, + Tags, + TagsOrUndefined, + _Tags, + _TagsOrUndefined, +} from '../../../common/schemas'; + +import { NamespaceType } from './types'; + +export interface ConstructorOptions { + user: string; + savedObjectsClient: SavedObjectsClientContract; +} + +export interface GetExceptionListOptions { + listId: ListIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; +} + +export interface CreateExceptionListOptions { + _tags: _Tags; + listId: ListId; + namespaceType: NamespaceType; + name: Name; + description: Description; + meta: MetaOrUndefined; + tags: Tags; + type: ExceptionListType; +} + +export interface UpdateExceptionListOptions { + _tags: _TagsOrUndefined; + id: IdOrUndefined; + listId: ListIdOrUndefined; + namespaceType: NamespaceType; + name: NameOrUndefined; + description: DescriptionOrUndefined; + meta: MetaOrUndefined; + tags: TagsOrUndefined; + type: ExceptionListTypeOrUndefined; +} + +export interface DeleteExceptionListOptions { + id: IdOrUndefined; + listId: ListIdOrUndefined; + namespaceType: NamespaceType; +} + +export interface DeleteExceptionListItemOptions { + id: IdOrUndefined; + itemId: ItemIdOrUndefined; + namespaceType: NamespaceType; +} + +export interface GetExceptionListItemOptions { + itemId: ItemIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; +} + +export interface CreateExceptionListItemOptions { + _tags: _Tags; + comment: CommentOrUndefined; + entries: EntriesArray; + itemId: ItemId; + listId: ListId; + namespaceType: NamespaceType; + name: Name; + description: Description; + meta: MetaOrUndefined; + tags: Tags; + type: ExceptionListType; +} + +export interface UpdateExceptionListItemOptions { + _tags: _TagsOrUndefined; + comment: CommentOrUndefined; + entries: EntriesArrayOrUndefined; + id: IdOrUndefined; + itemId: ItemIdOrUndefined; + namespaceType: NamespaceType; + name: NameOrUndefined; + description: DescriptionOrUndefined; + meta: MetaOrUndefined; + tags: TagsOrUndefined; + type: ExceptionListTypeOrUndefined; +} + +export interface FindExceptionListItemOptions { + listId: ListId; + namespaceType: NamespaceType; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} + +export interface FindExceptionListOptions { + namespaceType: NamespaceType; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts new file mode 100644 index 0000000000000..539dda673208b --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts @@ -0,0 +1,58 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/server'; + +import { ExceptionListSoSchema, FoundExceptionListSchema } from '../../../common/schemas'; +import { SavedObjectType } from '../../saved_objects'; + +import { getSavedObjectType, transformSavedObjectsToFounExceptionList } from './utils'; +import { NamespaceType } from './types'; + +interface FindExceptionListOptions { + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} + +export const findExceptionList = async ({ + namespaceType, + savedObjectsClient, + filter, + page, + perPage, + sortField, + sortOrder, +}: FindExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const savedObjectsFindResponse = await savedObjectsClient.find({ + filter: getExceptionListFilter({ filter, savedObjectType }), + page, + perPage, + sortField, + sortOrder, + type: savedObjectType, + }); + return transformSavedObjectsToFounExceptionList({ savedObjectsFindResponse }); +}; + +export const getExceptionListFilter = ({ + filter, + savedObjectType, +}: { + filter: string | undefined; + savedObjectType: SavedObjectType; +}): string => { + if (filter == null) { + return `${savedObjectType}.attributes.list_type: list`; + } else { + return `${savedObjectType}.attributes.list_type: list AND ${filter}`; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts new file mode 100644 index 0000000000000..d635cafbd3b1b --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts @@ -0,0 +1,77 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/server'; + +import { + ExceptionListSoSchema, + FoundExceptionListItemSchema, + ListId, +} from '../../../common/schemas'; +import { SavedObjectType } from '../../saved_objects'; + +import { getSavedObjectType, transformSavedObjectsToFounExceptionListItem } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionList } from './get_exception_list'; + +interface FindExceptionListItemOptions { + listId: ListId; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} + +export const findExceptionListItem = async ({ + listId, + namespaceType, + savedObjectsClient, + filter, + page, + perPage, + sortField, + sortOrder, +}: FindExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionList = await getExceptionList({ + id: undefined, + listId, + namespaceType, + savedObjectsClient, + }); + if (exceptionList == null) { + return null; + } else { + const savedObjectsFindResponse = await savedObjectsClient.find({ + filter: getExceptionListItemFilter({ filter, listId, savedObjectType }), + page, + perPage, + sortField, + sortOrder, + type: savedObjectType, + }); + return transformSavedObjectsToFounExceptionListItem({ savedObjectsFindResponse }); + } +}; + +export const getExceptionListItemFilter = ({ + filter, + listId, + savedObjectType, +}: { + listId: ListId; + filter: string | undefined; + savedObjectType: SavedObjectType; +}): string => { + if (filter == null) { + return `${savedObjectType}.attributes.list_type: item AND ${savedObjectType}.attributes.list_id: ${listId}`; + } else { + return `${savedObjectType}.attributes.list_type: item AND ${savedObjectType}.attributes.list_id: ${listId} AND ${filter}`; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts new file mode 100644 index 0000000000000..8b28443b4e30c --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts @@ -0,0 +1,64 @@ +/* + * 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 { + SavedObjectsClientContract, + SavedObjectsErrorHelpers, +} from '../../../../../../src/core/server/'; +import { + ExceptionListSchema, + ExceptionListSoSchema, + IdOrUndefined, + ListIdOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils'; +import { NamespaceType } from './types'; + +interface GetExceptionListOptions { + id: IdOrUndefined; + listId: ListIdOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +} + +export const getExceptionList = async ({ + id, + listId, + savedObjectsClient, + namespaceType, +}: GetExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + if (id != null) { + try { + const savedObject = await savedObjectsClient.get(savedObjectType, id); + return transformSavedObjectToExceptionList({ savedObject }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return null; + } else { + throw err; + } + } + } else if (listId != null) { + const savedObject = await savedObjectsClient.find({ + filter: `${savedObjectType}.attributes.list_type: list`, + perPage: 1, + search: listId, + searchFields: ['list_id'], + sortField: 'tie_breaker_id', + sortOrder: 'desc', + type: savedObjectType, + }); + if (savedObject.saved_objects[0] != null) { + return transformSavedObjectToExceptionList({ savedObject: savedObject.saved_objects[0] }); + } else { + return null; + } + } else { + return null; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts new file mode 100644 index 0000000000000..7ef3e4af3d604 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts @@ -0,0 +1,66 @@ +/* + * 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 { + SavedObjectsClientContract, + SavedObjectsErrorHelpers, +} from '../../../../../../src/core/server/'; +import { + ExceptionListItemSchema, + ExceptionListSoSchema, + IdOrUndefined, + ItemIdOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionListItem } from './utils'; +import { NamespaceType } from './types'; + +interface GetExceptionListItemOptions { + id: IdOrUndefined; + itemId: ItemIdOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +} + +export const getExceptionListItem = async ({ + id, + itemId, + savedObjectsClient, + namespaceType, +}: GetExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + if (id != null) { + try { + const savedObject = await savedObjectsClient.get(savedObjectType, id); + return transformSavedObjectToExceptionListItem({ savedObject }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return null; + } else { + throw err; + } + } + } else if (itemId != null) { + const savedObject = await savedObjectsClient.find({ + filter: `${savedObjectType}.attributes.list_type: item`, + perPage: 1, + search: itemId, + searchFields: ['item_id'], + sortField: 'tie_breaker_id', + sortOrder: 'desc', + type: savedObjectType, + }); + if (savedObject.saved_objects[0] != null) { + return transformSavedObjectToExceptionListItem({ + savedObject: savedObject.saved_objects[0], + }); + } else { + return null; + } + } else { + return null; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/index.ts b/x-pack/plugins/lists/server/services/exception_lists/index.ts new file mode 100644 index 0000000000000..a66f00819605b --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/index.ts @@ -0,0 +1,16 @@ +/* + * 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 './create_exception_list_item'; +export * from './create_exception_list'; +export * from './delete_exception_list_item'; +export * from './delete_exception_list'; +export * from './find_exception_list'; +export * from './find_exception_list_item'; +export * from './get_exception_list_item'; +export * from './get_exception_list'; +export * from './update_exception_list_item'; +export * from './update_exception_list'; diff --git a/x-pack/plugins/lists/server/services/exception_lists/types.ts b/x-pack/plugins/lists/server/services/exception_lists/types.ts new file mode 100644 index 0000000000000..dbb188bc2754a --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/types.ts @@ -0,0 +1,6 @@ +/* + * 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 type NamespaceType = 'agnostic' | 'single'; diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts new file mode 100644 index 0000000000000..6c5ccb5e1f2fd --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts @@ -0,0 +1,74 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/server'; + +import { + DescriptionOrUndefined, + ExceptionListSchema, + ExceptionListSoSchema, + ExceptionListTypeOrUndefined, + IdOrUndefined, + ListIdOrUndefined, + MetaOrUndefined, + NameOrUndefined, + TagsOrUndefined, + _TagsOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectUpdateToExceptionList } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionList } from './get_exception_list'; + +interface UpdateExceptionListOptions { + id: IdOrUndefined; + _tags: _TagsOrUndefined; + name: NameOrUndefined; + description: DescriptionOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + listId: ListIdOrUndefined; + meta: MetaOrUndefined; + user: string; + tags: TagsOrUndefined; + tieBreaker?: string; + type: ExceptionListTypeOrUndefined; +} + +export const updateExceptionList = async ({ + _tags, + id, + savedObjectsClient, + namespaceType, + name, + description, + listId, + meta, + user, + tags, + type, +}: UpdateExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionList = await getExceptionList({ id, listId, namespaceType, savedObjectsClient }); + if (exceptionList == null) { + return null; + } else { + const savedObject = await savedObjectsClient.update( + savedObjectType, + exceptionList.id, + { + _tags, + description, + meta, + name, + tags, + type, + updated_by: user, + } + ); + return transformSavedObjectUpdateToExceptionList({ exceptionList, savedObject }); + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts new file mode 100644 index 0000000000000..4e955d4281c4d --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts @@ -0,0 +1,87 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/server'; + +import { + CommentOrUndefined, + DescriptionOrUndefined, + EntriesArrayOrUndefined, + ExceptionListItemSchema, + ExceptionListSoSchema, + ExceptionListTypeOrUndefined, + IdOrUndefined, + ItemIdOrUndefined, + MetaOrUndefined, + NameOrUndefined, + TagsOrUndefined, + _TagsOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectUpdateToExceptionListItem } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionListItem } from './get_exception_list_item'; + +interface UpdateExceptionListItemOptions { + id: IdOrUndefined; + comment: CommentOrUndefined; + _tags: _TagsOrUndefined; + name: NameOrUndefined; + description: DescriptionOrUndefined; + entries: EntriesArrayOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + itemId: ItemIdOrUndefined; + meta: MetaOrUndefined; + user: string; + tags: TagsOrUndefined; + tieBreaker?: string; + type: ExceptionListTypeOrUndefined; +} + +export const updateExceptionListItem = async ({ + _tags, + comment, + entries, + id, + savedObjectsClient, + namespaceType, + name, + description, + itemId, + meta, + user, + tags, + type, +}: UpdateExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionListItem = await getExceptionListItem({ + id, + itemId, + namespaceType, + savedObjectsClient, + }); + if (exceptionListItem == null) { + return null; + } else { + const savedObject = await savedObjectsClient.update( + savedObjectType, + exceptionListItem.id, + { + _tags, + comment, + description, + entries, + meta, + name, + tags, + type, + updated_by: user, + } + ); + return transformSavedObjectUpdateToExceptionListItem({ exceptionListItem, savedObject }); + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils.ts b/x-pack/plugins/lists/server/services/exception_lists/utils.ts new file mode 100644 index 0000000000000..505ebc43afc5a --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/utils.ts @@ -0,0 +1,235 @@ +/* + * 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 { SavedObject, SavedObjectsFindResponse, SavedObjectsUpdateResponse } from 'kibana/server'; + +import { + ExceptionListItemSchema, + ExceptionListSchema, + ExceptionListSoSchema, + FoundExceptionListItemSchema, + FoundExceptionListSchema, +} from '../../../common/schemas'; +import { + SavedObjectType, + exceptionListAgnosticSavedObjectType, + exceptionListSavedObjectType, +} from '../../saved_objects'; + +import { NamespaceType } from './types'; + +export const getSavedObjectType = ({ + namespaceType, +}: { + namespaceType: NamespaceType; +}): SavedObjectType => { + if (namespaceType === 'agnostic') { + return exceptionListAgnosticSavedObjectType; + } else { + return exceptionListSavedObjectType; + } +}; + +export const transformSavedObjectToExceptionList = ({ + savedObject, +}: { + savedObject: SavedObject; +}): ExceptionListSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { + _tags, + created_at, + created_by, + description, + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_by, + }, + id, + updated_at: updatedAt, + } = savedObject; + + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: list" + return { + _tags, + created_at, + created_by, + description, + id, + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_at: updatedAt ?? dateNow, + updated_by, + }; +}; + +export const transformSavedObjectUpdateToExceptionList = ({ + exceptionList, + savedObject, +}: { + exceptionList: ExceptionListSchema; + savedObject: SavedObjectsUpdateResponse; +}): ExceptionListSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { _tags, description, meta, name, tags, type, updated_by: updatedBy }, + id, + updated_at: updatedAt, + } = savedObject; + + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: list" + return { + _tags: _tags ?? exceptionList._tags, + created_at: exceptionList.created_at, + created_by: exceptionList.created_by, + description: description ?? exceptionList.description, + id, + list_id: exceptionList.list_id, + meta: meta ?? exceptionList.meta, + name: name ?? exceptionList.name, + tags: tags ?? exceptionList.tags, + tie_breaker_id: exceptionList.tie_breaker_id, + type: type ?? exceptionList.type, + updated_at: updatedAt ?? dateNow, + updated_by: updatedBy ?? exceptionList.updated_by, + }; +}; + +export const transformSavedObjectToExceptionListItem = ({ + savedObject, +}: { + savedObject: SavedObject; +}): ExceptionListItemSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { + _tags, + comment, + created_at, + created_by, + description, + entries, + item_id: itemId, + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_by, + }, + id, + updated_at: updatedAt, + } = savedObject; + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: item" + // TODO: Do a throw if item_id or entries is not defined. + return { + _tags, + comment, + created_at, + created_by, + description, + entries: entries ?? [], + id, + item_id: itemId ?? '(unknown)', + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_at: updatedAt ?? dateNow, + updated_by, + }; +}; + +export const transformSavedObjectUpdateToExceptionListItem = ({ + exceptionListItem, + savedObject, +}: { + exceptionListItem: ExceptionListItemSchema; + savedObject: SavedObjectsUpdateResponse; +}): ExceptionListItemSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { + _tags, + comment, + description, + entries, + meta, + name, + tags, + type, + updated_by: updatedBy, + }, + id, + updated_at: updatedAt, + } = savedObject; + + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: list" + return { + _tags: _tags ?? exceptionListItem._tags, + comment: comment ?? exceptionListItem.comment, + created_at: exceptionListItem.created_at, + created_by: exceptionListItem.created_by, + description: description ?? exceptionListItem.description, + entries: entries ?? exceptionListItem.entries, + id, + item_id: exceptionListItem.item_id, + list_id: exceptionListItem.list_id, + meta: meta ?? exceptionListItem.meta, + name: name ?? exceptionListItem.name, + tags: tags ?? exceptionListItem.tags, + tie_breaker_id: exceptionListItem.tie_breaker_id, + type: type ?? exceptionListItem.type, + updated_at: updatedAt ?? dateNow, + updated_by: updatedBy ?? exceptionListItem.updated_by, + }; +}; + +export const transformSavedObjectsToFounExceptionListItem = ({ + savedObjectsFindResponse, +}: { + savedObjectsFindResponse: SavedObjectsFindResponse; +}): FoundExceptionListItemSchema => { + return { + data: savedObjectsFindResponse.saved_objects.map(savedObject => + transformSavedObjectToExceptionListItem({ savedObject }) + ), + page: savedObjectsFindResponse.page, + per_page: savedObjectsFindResponse.per_page, + total: savedObjectsFindResponse.total, + }; +}; + +export const transformSavedObjectsToFounExceptionList = ({ + savedObjectsFindResponse, +}: { + savedObjectsFindResponse: SavedObjectsFindResponse; +}): FoundExceptionListSchema => { + return { + data: savedObjectsFindResponse.saved_objects.map(savedObject => + transformSavedObjectToExceptionList({ savedObject }) + ), + page: savedObjectsFindResponse.page, + per_page: savedObjectsFindResponse.per_page, + total: savedObjectsFindResponse.total, + }; +}; diff --git a/x-pack/plugins/lists/server/services/lists/client.ts b/x-pack/plugins/lists/server/services/lists/list_client.ts similarity index 99% rename from x-pack/plugins/lists/server/services/lists/client.ts rename to x-pack/plugins/lists/server/services/lists/list_client.ts index ba22bf72cc18c..cba48115c746c 100644 --- a/x-pack/plugins/lists/server/services/lists/client.ts +++ b/x-pack/plugins/lists/server/services/lists/list_client.ts @@ -59,7 +59,7 @@ import { ImportListItemsToStreamOptions, UpdateListItemOptions, UpdateListOptions, -} from './client_types'; +} from './list_client_types'; export class ListClient { private readonly spaceId: string; diff --git a/x-pack/plugins/lists/server/services/lists/client_types.ts b/x-pack/plugins/lists/server/services/lists/list_client_types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/client_types.ts rename to x-pack/plugins/lists/server/services/lists/list_client_types.ts diff --git a/x-pack/plugins/lists/server/services/utils/index.ts b/x-pack/plugins/lists/server/services/utils/index.ts index 8a44b5ab607bf..e6365e689f761 100644 --- a/x-pack/plugins/lists/server/services/utils/index.ts +++ b/x-pack/plugins/lists/server/services/utils/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +export * from './derive_type_from_es_type'; export * from './get_query_filter_from_type_value'; export * from './transform_elastic_to_list_item'; export * from './transform_list_item_to_elastic_query'; -export * from './derive_type_from_es_type'; diff --git a/x-pack/plugins/lists/server/siem_server_deps.ts b/x-pack/plugins/lists/server/siem_server_deps.ts index 6231b2e014111..81ebe9f17b06f 100644 --- a/x-pack/plugins/lists/server/siem_server_deps.ts +++ b/x-pack/plugins/lists/server/siem_server_deps.ts @@ -6,27 +6,16 @@ export { transformError, + deleteTemplate, + deletePolicy, + deleteAllIndex, + setPolicy, + setTemplate, buildSiemResponse, -} from '../../siem/server/lib/detection_engine/routes/utils'; // eslint-disable-line @kbn/eslint/no-restricted-paths -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { deleteTemplate } from '../../siem/server/lib/detection_engine/index/delete_template'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { deletePolicy } from '../../siem/server/lib/detection_engine/index/delete_policy'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { deleteAllIndex } from '../../siem/server/lib/detection_engine/index/delete_all_index'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { setPolicy } from '../../siem/server/lib/detection_engine/index/set_policy'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { setTemplate } from '../../siem/server/lib/detection_engine/index/set_template'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { getTemplateExists } from '../../siem/server/lib/detection_engine/index/get_template_exists'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { getPolicyExists } from '../../siem/server/lib/detection_engine/index/get_policy_exists'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { createBootstrapIndex } from '../../siem/server/lib/detection_engine/index/create_bootstrap_index'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { getIndexExists } from '../../siem/server/lib/detection_engine/index/get_index_exists'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { buildRouteValidation } from '../../siem/server/utils/build_validation/route_validation'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { validate } from '../../siem/server/lib/detection_engine/routes/rules/validate'; + getTemplateExists, + getPolicyExists, + createBootstrapIndex, + getIndexExists, + buildRouteValidation, + validate, +} from '../../siem/server'; diff --git a/x-pack/plugins/lists/server/types.ts b/x-pack/plugins/lists/server/types.ts index d7c3208e556fa..6808aaa04ab7a 100644 --- a/x-pack/plugins/lists/server/types.ts +++ b/x-pack/plugins/lists/server/types.ts @@ -4,12 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { APICaller, IContextProvider, RequestHandler } from 'kibana/server'; +import { + APICaller, + IContextProvider, + RequestHandler, + SavedObjectsClientContract, +} from 'kibana/server'; import { SecurityPluginSetup } from '../../security/server'; import { SpacesPluginSetup } from '../../spaces/server'; -import { ListClient } from './services/lists/client'; +import { ListClient } from './services/lists/list_client'; +import { ExceptionListClient } from './services/exception_lists/exception_list_client'; export type ContextProvider = IContextProvider, 'lists'>; export type ListsPluginStart = void; @@ -23,14 +29,25 @@ export type GetListClientType = ( spaceId: string, user: string ) => ListClient; + +export type GetExceptionListClientType = ( + savedObjectsClient: SavedObjectsClientContract, + user: string +) => ExceptionListClient; + export interface ListPluginSetup { + getExceptionListClient: GetExceptionListClientType; getListClient: GetListClientType; } -export type ContextProviderReturn = Promise<{ getListClient: () => ListClient }>; +export type ContextProviderReturn = Promise<{ + getListClient: () => ListClient; + getExceptionListClient: () => ExceptionListClient; +}>; declare module 'src/core/server' { interface RequestHandlerContext { lists?: { + getExceptionListClient: () => ExceptionListClient; getListClient: () => ListClient; }; } diff --git a/x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts b/x-pack/plugins/siem/common/exact_check.test.ts similarity index 98% rename from x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts rename to x-pack/plugins/siem/common/exact_check.test.ts index 1e70deaeed438..a5701603de0d6 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts +++ b/x-pack/plugins/siem/common/exact_check.test.ts @@ -8,8 +8,8 @@ import * as t from 'io-ts'; import { left, right, Either } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from './__mocks__/utils'; import { exactCheck, findDifferencesRecursive } from './exact_check'; +import { foldLeftRight, getPaths } from './test_utils'; describe('exact_check', () => { test('it returns an error if given extra object properties', () => { diff --git a/x-pack/plugins/siem/server/utils/build_validation/exact_check.ts b/x-pack/plugins/siem/common/exact_check.ts similarity index 100% rename from x-pack/plugins/siem/server/utils/build_validation/exact_check.ts rename to x-pack/plugins/siem/common/exact_check.ts diff --git a/x-pack/plugins/siem/server/utils/build_validation/format_errors.test.ts b/x-pack/plugins/siem/common/format_errors.test.ts similarity index 100% rename from x-pack/plugins/siem/server/utils/build_validation/format_errors.test.ts rename to x-pack/plugins/siem/common/format_errors.test.ts diff --git a/x-pack/plugins/siem/server/utils/build_validation/format_errors.ts b/x-pack/plugins/siem/common/format_errors.ts similarity index 100% rename from x-pack/plugins/siem/server/utils/build_validation/format_errors.ts rename to x-pack/plugins/siem/common/format_errors.ts diff --git a/x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts b/x-pack/plugins/siem/common/test_utils.ts similarity index 95% rename from x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts rename to x-pack/plugins/siem/common/test_utils.ts index 578972dda5aef..29b0119dcbeb7 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts +++ b/x-pack/plugins/siem/common/test_utils.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { formatErrors } from '../format_errors'; +import { formatErrors } from './format_errors'; interface Message { errors: t.Errors; diff --git a/x-pack/plugins/siem/server/index.ts b/x-pack/plugins/siem/server/index.ts index e9cd78589fac9..586b9dec2f4ab 100644 --- a/x-pack/plugins/siem/server/index.ts +++ b/x-pack/plugins/siem/server/index.ts @@ -15,3 +15,17 @@ export const plugin = (context: PluginInitializerContext) => { export const config = { schema: configSchema }; export { ConfigType, Plugin, PluginSetup, PluginStart }; + +// Exports to be shared with plugins such as x-pack/lists plugin +export { deleteTemplate } from './lib/detection_engine/index/delete_template'; +export { deletePolicy } from './lib/detection_engine/index/delete_policy'; +export { deleteAllIndex } from './lib/detection_engine/index/delete_all_index'; +export { setPolicy } from './lib/detection_engine/index/set_policy'; +export { setTemplate } from './lib/detection_engine/index/set_template'; +export { getTemplateExists } from './lib/detection_engine/index/get_template_exists'; +export { getPolicyExists } from './lib/detection_engine/index/get_policy_exists'; +export { createBootstrapIndex } from './lib/detection_engine/index/create_bootstrap_index'; +export { getIndexExists } from './lib/detection_engine/index/get_index_exists'; +export { buildRouteValidation } from './utils/build_validation/route_validation'; +export { validate } from './lib/detection_engine/routes/rules/validate'; +export { transformError, buildSiemResponse } from './lib/detection_engine/routes/utils'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts index cfba40fc225a2..cda3a4b81ed9b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts @@ -9,8 +9,8 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as t from 'io-ts'; -import { formatErrors } from '../../../../utils/build_validation/format_errors'; -import { exactCheck } from '../../../../utils/build_validation/exact_check'; +import { formatErrors } from '../../../../../common/format_errors'; +import { exactCheck } from '../../../../../common/exact_check'; import { PartialAlert, FindResult } from '../../../../../../alerting/server'; import { isAlertType, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts index fbd2382e2826d..0b0d3bf43b1e9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts @@ -20,8 +20,8 @@ import { left } from 'fp-ts/lib/Either'; import { RulesSchema } from './rules_schema'; import { TypeAndTimelineOnly } from './type_timeline_only_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('check_type_dependents', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts index 6e159a792edb6..9bbde3d5236db 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts @@ -10,8 +10,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { getErrorPayload } from './__mocks__/utils'; import { errorSchema, ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('error_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts index 68b67db595d76..1b7d7994462c7 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts @@ -10,8 +10,8 @@ import { getFindResponseSingle, getBaseResponsePayload } from './__mocks__/utils import { left } from 'fp-ts/lib/Either'; import { RulesSchema } from './rules_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { getPaths, foldLeftRight } from '../../../../../utils/build_validation/__mocks__/utils'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../../../../common/exact_check'; describe('find_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts index b0b863ebbbc0b..18e17a319883a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts @@ -9,9 +9,9 @@ import { left, Either } from 'fp-ts/lib/Either'; import { ImportRulesSchema, importRulesSchema } from './import_rules_schema'; import { ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { getPaths, foldLeftRight } from '../../../../../utils/build_validation/__mocks__/utils'; import { Errors } from 'io-ts'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('import_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts index 827167c63fd58..2d3fd75914822 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts @@ -8,8 +8,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; import { PrePackagedRulesSchema, prePackagedRulesSchema } from './prepackaged_rules_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('prepackaged_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts index a864667583c0a..abe601a546111 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts @@ -11,8 +11,8 @@ import { prePackagedRulesStatusSchema, } from './prepackaged_rules_status_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('prepackaged_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts index 9a7cf5e2c2871..98cb2ef058485 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts @@ -12,8 +12,8 @@ import { RulesBulkSchema, rulesBulkSchema } from './rules_bulk_schema'; import { RulesSchema } from './rules_schema'; import { ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('prepackaged_rule_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts index 82a6682e6461f..ade4d12517aca 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts @@ -10,8 +10,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { rulesSchema, RulesSchema, removeList } from './rules_schema'; import { getBaseResponsePayload } from './__mocks__/utils'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../../../../common/exact_check'; export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts index 85fb124464487..8f06e2c6e49b0 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts @@ -9,8 +9,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { TypeAndTimelineOnly, typeAndTimelineOnlySchema } from './type_timeline_only_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('prepackaged_rule_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts index ff62ea4443f3f..9f9181359d44a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts @@ -7,7 +7,7 @@ import { IsoDateString } from './iso_date_string'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('ios_date_string', () => { test('it should validate a iso string', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts index 2a97c8a4a143e..dc0bd6cacf0d6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts @@ -7,7 +7,7 @@ import { ListsDefaultArray } from './lists_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('lists_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts index d6f21681df88f..a3338c878bd71 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts @@ -7,7 +7,7 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_zero'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts index 26441745a7f29..48ea2025b9b12 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts @@ -7,7 +7,7 @@ import { PositiveInteger } from './positive_integer'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts index 76f722274ce23..3aaff7e00ad51 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts @@ -7,7 +7,7 @@ import { ReferencesDefaultArray } from './references_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('references_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts index 76e3445358dd9..41c0faf4d608d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts @@ -7,7 +7,7 @@ import { RiskScore } from './risk_score'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('risk_score', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts index 7b68dbcef2d7e..b640b449e6b8a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts @@ -7,7 +7,7 @@ import { UUID } from './uuid'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('uuid', () => { test('it should validate a uuid', () => { diff --git a/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts b/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts index 30b95dcfa94ee..19353080c15cb 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts +++ b/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts @@ -7,13 +7,13 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; +import { formatErrors } from '../../../common/format_errors'; +import { exactCheck } from '../../../common/exact_check'; import { RouteValidationFunction, RouteValidationResultFactory, RouteValidationError, } from '../../../../../../src/core/server'; -import { exactCheck } from './exact_check'; -import { formatErrors } from './format_errors'; type RequestValidationResult = | { From 8dc8aa0c64317e0987849f37d4f83cd324d12922 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 15 May 2020 14:18:39 -0600 Subject: [PATCH 05/14] [Maps] Handle cross cluster index _settings resp (#66797) --- .../plugins/maps/server/lib/get_index_pattern_settings.js | 6 +++++- .../maps/server/lib/get_index_pattern_settings.test.js | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js index e2a758075155a..b7cd1687dc257 100644 --- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js +++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js @@ -29,5 +29,9 @@ export function getIndexPatternSettings(indicesSettingsResp) { maxInnerResultWindow = Math.min(indexMaxInnerResultWindow, indexMaxResultWindow); }); - return { maxResultWindow, maxInnerResultWindow }; + return { + maxResultWindow: maxResultWindow === Infinity ? DEFAULT_MAX_RESULT_WINDOW : maxResultWindow, + maxInnerResultWindow: + maxInnerResultWindow === Infinity ? DEFAULT_MAX_INNER_RESULT_WINDOW : maxInnerResultWindow, + }; } diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js index c152f5bfffc31..46949a2e3e2cf 100644 --- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js +++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js @@ -24,6 +24,14 @@ describe('max_result_window and max_inner_result_window are not set', () => { expect(maxInnerResultWindow).toBe(DEFAULT_MAX_INNER_RESULT_WINDOW); }); + test('Should provide default values from cross cluster index response', () => { + // _settings returns empty object for cross cluster index + const indicesSettingsResp = {}; + const { maxResultWindow, maxInnerResultWindow } = getIndexPatternSettings(indicesSettingsResp); + expect(maxResultWindow).toBe(DEFAULT_MAX_RESULT_WINDOW); + expect(maxInnerResultWindow).toBe(DEFAULT_MAX_INNER_RESULT_WINDOW); + }); + test('Should include default values when providing minimum values for indices in index pattern', () => { const indicesSettingsResp = { kibana_sample_data_logs: { From a8e82118c8742a444cfcb751e57571db64955025 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 15 May 2020 21:32:27 +0100 Subject: [PATCH 06/14] chore(NA): bump static-fs to 1.0.2 (#66775) Co-authored-by: Elastic Machine --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 91034fea5156a..72a63ec34d2c7 100644 --- a/package.json +++ b/package.json @@ -298,7 +298,7 @@ "@elastic/eslint-plugin-eui": "0.0.2", "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^5.0.1", - "@elastic/static-fs": "1.0.1", + "@elastic/static-fs": "1.0.2", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/eslint-import-resolver-kibana": "2.0.0", diff --git a/yarn.lock b/yarn.lock index 245e0de152658..a18f89cd480f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1427,10 +1427,10 @@ "@types/node-jose" "1.1.0" node-jose "1.1.0" -"@elastic/static-fs@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@elastic/static-fs/-/static-fs-1.0.1.tgz#2e084e9fc321dd4c7fb4579021ca8a6b26f3464e" - integrity sha512-Vl40Va/h0P6aDUrzgDeTabGVUb/s/W92le64E1UXTcG5927cZtTnOu0datMjr98xdr9C6RAJ3mr6zgxfox5TNw== +"@elastic/static-fs@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@elastic/static-fs/-/static-fs-1.0.2.tgz#c1e5fea6a1b35abcd005cecf7880156ed0f273ae" + integrity sha512-0cZc5D9Wg6pJsc8Sa2ns1eOuxtXEidE7GBb2B0KZdJq9nZzUCxMyplURqT0Nr3i5XpoHb6ZEmxWsji86j1KjDw== "@elastic/ui-ace@0.2.3": version "0.2.3" From 3228f1d1371da58865ce2c6ff486d3a15d3c4e85 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Fri, 15 May 2020 15:59:59 -0600 Subject: [PATCH 07/14] [SIEM] Fixes glob patterns from directory changes recently for GraphQL ## Summary Our directory structure has changed recently and the GraphQL needs to be updated for it to find the new location of the graphql files. I used a more global glob approach which will make it easier to change directories again and not have it break. This does cause the generated file to have different positions and looks like changes but they are just movements. To test this: * Check it out * Run this: ```sh cd $HOME/projects/kibana/x-pack/plugins/siem && node scripts/generate_types_from_graphql.js ``` And ensure the types do not change and you can run a type check like so: ```sh cd $HOME/projects/kibana && node scripts/type_check.js --project x-pack/tsconfig.json ``` --- x-pack/plugins/siem/public/graphql/types.ts | 1194 ++++++++--------- .../scripts/generate_types_from_graphql.js | 2 +- 2 files changed, 598 insertions(+), 598 deletions(-) diff --git a/x-pack/plugins/siem/public/graphql/types.ts b/x-pack/plugins/siem/public/graphql/types.ts index 3436ee84a2f30..c3493c580fa22 100644 --- a/x-pack/plugins/siem/public/graphql/types.ts +++ b/x-pack/plugins/siem/public/graphql/types.ts @@ -2541,6 +2541,140 @@ export interface DeleteTimelineMutationArgs { // Documents // ==================================================== +export namespace GetLastEventTimeQuery { + export type Variables = { + sourceId: string; + indexKey: LastEventIndexKey; + details: LastTimeDetails; + defaultIndex: string[]; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + LastEventTime: LastEventTime; + }; + + export type LastEventTime = { + __typename?: 'LastEventTimeData'; + + lastSeen: Maybe; + }; +} + +export namespace GetMatrixHistogramQuery { + export type Variables = { + defaultIndex: string[]; + filterQuery?: Maybe; + histogramType: HistogramType; + inspect: boolean; + sourceId: string; + stackByField: string; + timerange: TimerangeInput; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + MatrixHistogram: MatrixHistogram; + }; + + export type MatrixHistogram = { + __typename?: 'MatrixHistogramOverTimeData'; + + matrixHistogramData: MatrixHistogramData[]; + + totalCount: number; + + inspect: Maybe; + }; + + export type MatrixHistogramData = { + __typename?: 'MatrixOverTimeHistogramData'; + + x: Maybe; + + y: Maybe; + + g: Maybe; + }; + + export type Inspect = { + __typename?: 'Inspect'; + + dsl: string[]; + + response: string[]; + }; +} + +export namespace SourceQuery { + export type Variables = { + sourceId?: Maybe; + defaultIndex: string[]; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + status: Status; + }; + + export type Status = { + __typename?: 'SourceStatus'; + + indicesExist: boolean; + + indexFields: IndexFields[]; + }; + + export type IndexFields = { + __typename?: 'IndexField'; + + category: string; + + description: Maybe; + + example: Maybe; + + indexes: (Maybe)[]; + + name: string; + + searchable: boolean; + + type: string; + + aggregatable: boolean; + + format: Maybe; + }; +} + export namespace GetAuthenticationsQuery { export type Variables = { sourceId: string; @@ -2680,35 +2814,6 @@ export namespace GetAuthenticationsQuery { }; } -export namespace GetLastEventTimeQuery { - export type Variables = { - sourceId: string; - indexKey: LastEventIndexKey; - details: LastTimeDetails; - defaultIndex: string[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - LastEventTime: LastEventTime; - }; - - export type LastEventTime = { - __typename?: 'LastEventTimeData'; - - lastSeen: Maybe; - }; -} - export namespace GetHostFirstLastSeenQuery { export type Variables = { sourceId: string; @@ -2935,11 +3040,11 @@ export namespace GetHostOverviewQuery { }; } -export namespace GetIpOverviewQuery { +export namespace GetKpiHostDetailsQuery { export type Variables = { sourceId: string; + timerange: TimerangeInput; filterQuery?: Maybe; - ip: string; defaultIndex: string[]; inspect: boolean; }; @@ -2955,154 +3060,106 @@ export namespace GetIpOverviewQuery { id: string; - IpOverview: Maybe; + KpiHostDetails: KpiHostDetails; }; - export type IpOverview = { - __typename?: 'IpOverviewData'; + export type KpiHostDetails = { + __typename?: 'KpiHostDetailsData'; - source: Maybe<_Source>; + authSuccess: Maybe; - destination: Maybe; + authSuccessHistogram: Maybe; - host: Host; + authFailure: Maybe; - inspect: Maybe; - }; + authFailureHistogram: Maybe; - export type _Source = { - __typename?: 'Overview'; + uniqueSourceIps: Maybe; - firstSeen: Maybe; + uniqueSourceIpsHistogram: Maybe; - lastSeen: Maybe; + uniqueDestinationIps: Maybe; - autonomousSystem: AutonomousSystem; + uniqueDestinationIpsHistogram: Maybe; - geo: Geo; + inspect: Maybe; }; - export type AutonomousSystem = { - __typename?: 'AutonomousSystem'; + export type AuthSuccessHistogram = KpiHostDetailsChartFields.Fragment; - number: Maybe; + export type AuthFailureHistogram = KpiHostDetailsChartFields.Fragment; - organization: Maybe; - }; + export type UniqueSourceIpsHistogram = KpiHostDetailsChartFields.Fragment; - export type Organization = { - __typename?: 'AutonomousSystemOrganization'; + export type UniqueDestinationIpsHistogram = KpiHostDetailsChartFields.Fragment; - name: Maybe; - }; + export type Inspect = { + __typename?: 'Inspect'; - export type Geo = { - __typename?: 'GeoEcsFields'; + dsl: string[]; - continent_name: Maybe; + response: string[]; + }; +} - city_name: Maybe; +export namespace GetKpiHostsQuery { + export type Variables = { + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; + defaultIndex: string[]; + inspect: boolean; + }; - country_iso_code: Maybe; + export type Query = { + __typename?: 'Query'; - country_name: Maybe; + source: Source; + }; - location: Maybe; + export type Source = { + __typename?: 'Source'; - region_iso_code: Maybe; + id: string; - region_name: Maybe; + KpiHosts: KpiHosts; }; - export type Location = { - __typename?: 'Location'; + export type KpiHosts = { + __typename?: 'KpiHostsData'; - lat: Maybe; + hosts: Maybe; - lon: Maybe; - }; + hostsHistogram: Maybe; - export type Destination = { - __typename?: 'Overview'; + authSuccess: Maybe; - firstSeen: Maybe; + authSuccessHistogram: Maybe; - lastSeen: Maybe; + authFailure: Maybe; - autonomousSystem: _AutonomousSystem; + authFailureHistogram: Maybe; - geo: _Geo; - }; - - export type _AutonomousSystem = { - __typename?: 'AutonomousSystem'; - - number: Maybe; - - organization: Maybe<_Organization>; - }; - - export type _Organization = { - __typename?: 'AutonomousSystemOrganization'; - - name: Maybe; - }; - - export type _Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - city_name: Maybe; - - country_iso_code: Maybe; - - country_name: Maybe; - - location: Maybe<_Location>; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type _Location = { - __typename?: 'Location'; - - lat: Maybe; - - lon: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - architecture: Maybe; - - id: Maybe; - - ip: Maybe; + uniqueSourceIps: Maybe; - mac: Maybe; + uniqueSourceIpsHistogram: Maybe; - name: Maybe; + uniqueDestinationIps: Maybe; - os: Maybe; + uniqueDestinationIpsHistogram: Maybe; - type: Maybe; + inspect: Maybe; }; - export type Os = { - __typename?: 'OsEcsFields'; + export type HostsHistogram = KpiHostChartFields.Fragment; - family: Maybe; + export type AuthSuccessHistogram = KpiHostChartFields.Fragment; - name: Maybe; + export type AuthFailureHistogram = KpiHostChartFields.Fragment; - platform: Maybe; + export type UniqueSourceIpsHistogram = KpiHostChartFields.Fragment; - version: Maybe; - }; + export type UniqueDestinationIpsHistogram = KpiHostChartFields.Fragment; export type Inspect = { __typename?: 'Inspect'; @@ -3113,10 +3170,11 @@ export namespace GetIpOverviewQuery { }; } -export namespace GetKpiHostDetailsQuery { +export namespace GetUncommonProcessesQuery { export type Variables = { sourceId: string; timerange: TimerangeInput; + pagination: PaginationInputPaginated; filterQuery?: Maybe; defaultIndex: string[]; inspect: boolean; @@ -3133,106 +3191,80 @@ export namespace GetKpiHostDetailsQuery { id: string; - KpiHostDetails: KpiHostDetails; + UncommonProcesses: UncommonProcesses; }; - export type KpiHostDetails = { - __typename?: 'KpiHostDetailsData'; - - authSuccess: Maybe; - - authSuccessHistogram: Maybe; - - authFailure: Maybe; - - authFailureHistogram: Maybe; - - uniqueSourceIps: Maybe; + export type UncommonProcesses = { + __typename?: 'UncommonProcessesData'; - uniqueSourceIpsHistogram: Maybe; + totalCount: number; - uniqueDestinationIps: Maybe; + edges: Edges[]; - uniqueDestinationIpsHistogram: Maybe; + pageInfo: PageInfo; inspect: Maybe; }; - export type AuthSuccessHistogram = KpiHostDetailsChartFields.Fragment; - - export type AuthFailureHistogram = KpiHostDetailsChartFields.Fragment; - - export type UniqueSourceIpsHistogram = KpiHostDetailsChartFields.Fragment; - - export type UniqueDestinationIpsHistogram = KpiHostDetailsChartFields.Fragment; - - export type Inspect = { - __typename?: 'Inspect'; + export type Edges = { + __typename?: 'UncommonProcessesEdges'; - dsl: string[]; + node: Node; - response: string[]; + cursor: Cursor; }; -} -export namespace GetKpiHostsQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; + export type Node = { + __typename?: 'UncommonProcessItem'; - export type Query = { - __typename?: 'Query'; + _id: string; - source: Source; - }; + instances: number; - export type Source = { - __typename?: 'Source'; + process: Process; - id: string; + user: Maybe; - KpiHosts: KpiHosts; + hosts: Hosts[]; }; - export type KpiHosts = { - __typename?: 'KpiHostsData'; - - hosts: Maybe; - - hostsHistogram: Maybe; + export type Process = { + __typename?: 'ProcessEcsFields'; - authSuccess: Maybe; + args: Maybe; - authSuccessHistogram: Maybe; + name: Maybe; + }; - authFailure: Maybe; + export type User = { + __typename?: 'UserEcsFields'; - authFailureHistogram: Maybe; + id: Maybe; - uniqueSourceIps: Maybe; + name: Maybe; + }; - uniqueSourceIpsHistogram: Maybe; + export type Hosts = { + __typename?: 'HostEcsFields'; - uniqueDestinationIps: Maybe; + name: Maybe; + }; - uniqueDestinationIpsHistogram: Maybe; + export type Cursor = { + __typename?: 'CursorType'; - inspect: Maybe; + value: Maybe; }; - export type HostsHistogram = KpiHostChartFields.Fragment; - - export type AuthSuccessHistogram = KpiHostChartFields.Fragment; + export type PageInfo = { + __typename?: 'PageInfoPaginated'; - export type AuthFailureHistogram = KpiHostChartFields.Fragment; + activePage: number; - export type UniqueSourceIpsHistogram = KpiHostChartFields.Fragment; + fakeTotalCount: number; - export type UniqueDestinationIpsHistogram = KpiHostChartFields.Fragment; + showMorePagesIndicator: boolean; + }; export type Inspect = { __typename?: 'Inspect'; @@ -3243,11 +3275,11 @@ export namespace GetKpiHostsQuery { }; } -export namespace GetKpiNetworkQuery { +export namespace GetIpOverviewQuery { export type Variables = { sourceId: string; - timerange: TimerangeInput; filterQuery?: Maybe; + ip: string; defaultIndex: string[]; inspect: boolean; }; @@ -3263,89 +3295,213 @@ export namespace GetKpiNetworkQuery { id: string; - KpiNetwork: Maybe; + IpOverview: Maybe; }; - export type KpiNetwork = { - __typename?: 'KpiNetworkData'; - - networkEvents: Maybe; - - uniqueFlowId: Maybe; - - uniqueSourcePrivateIps: Maybe; - - uniqueSourcePrivateIpsHistogram: Maybe; - - uniqueDestinationPrivateIps: Maybe; + export type IpOverview = { + __typename?: 'IpOverviewData'; - uniqueDestinationPrivateIpsHistogram: Maybe; + source: Maybe<_Source>; - dnsQueries: Maybe; + destination: Maybe; - tlsHandshakes: Maybe; + host: Host; inspect: Maybe; }; - export type UniqueSourcePrivateIpsHistogram = KpiNetworkChartFields.Fragment; + export type _Source = { + __typename?: 'Overview'; - export type UniqueDestinationPrivateIpsHistogram = KpiNetworkChartFields.Fragment; + firstSeen: Maybe; - export type Inspect = { - __typename?: 'Inspect'; + lastSeen: Maybe; - dsl: string[]; + autonomousSystem: AutonomousSystem; - response: string[]; + geo: Geo; }; -} -export namespace GetMatrixHistogramQuery { - export type Variables = { - defaultIndex: string[]; - filterQuery?: Maybe; - histogramType: HistogramType; - inspect: boolean; - sourceId: string; - stackByField: string; - timerange: TimerangeInput; - }; + export type AutonomousSystem = { + __typename?: 'AutonomousSystem'; - export type Query = { - __typename?: 'Query'; + number: Maybe; - source: Source; + organization: Maybe; }; - export type Source = { - __typename?: 'Source'; - - id: string; + export type Organization = { + __typename?: 'AutonomousSystemOrganization'; - MatrixHistogram: MatrixHistogram; + name: Maybe; }; - export type MatrixHistogram = { - __typename?: 'MatrixHistogramOverTimeData'; + export type Geo = { + __typename?: 'GeoEcsFields'; - matrixHistogramData: MatrixHistogramData[]; + continent_name: Maybe; - totalCount: number; + city_name: Maybe; - inspect: Maybe; - }; + country_iso_code: Maybe; - export type MatrixHistogramData = { - __typename?: 'MatrixOverTimeHistogramData'; + country_name: Maybe; - x: Maybe; + location: Maybe; - y: Maybe; + region_iso_code: Maybe; - g: Maybe; + region_name: Maybe; + }; + + export type Location = { + __typename?: 'Location'; + + lat: Maybe; + + lon: Maybe; + }; + + export type Destination = { + __typename?: 'Overview'; + + firstSeen: Maybe; + + lastSeen: Maybe; + + autonomousSystem: _AutonomousSystem; + + geo: _Geo; + }; + + export type _AutonomousSystem = { + __typename?: 'AutonomousSystem'; + + number: Maybe; + + organization: Maybe<_Organization>; + }; + + export type _Organization = { + __typename?: 'AutonomousSystemOrganization'; + + name: Maybe; + }; + + export type _Geo = { + __typename?: 'GeoEcsFields'; + + continent_name: Maybe; + + city_name: Maybe; + + country_iso_code: Maybe; + + country_name: Maybe; + + location: Maybe<_Location>; + + region_iso_code: Maybe; + + region_name: Maybe; + }; + + export type _Location = { + __typename?: 'Location'; + + lat: Maybe; + + lon: Maybe; + }; + + export type Host = { + __typename?: 'HostEcsFields'; + + architecture: Maybe; + + id: Maybe; + + ip: Maybe; + + mac: Maybe; + + name: Maybe; + + os: Maybe; + + type: Maybe; + }; + + export type Os = { + __typename?: 'OsEcsFields'; + + family: Maybe; + + name: Maybe; + + platform: Maybe; + + version: Maybe; + }; + + export type Inspect = { + __typename?: 'Inspect'; + + dsl: string[]; + + response: string[]; + }; +} + +export namespace GetKpiNetworkQuery { + export type Variables = { + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; + defaultIndex: string[]; + inspect: boolean; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + KpiNetwork: Maybe; + }; + + export type KpiNetwork = { + __typename?: 'KpiNetworkData'; + + networkEvents: Maybe; + + uniqueFlowId: Maybe; + + uniqueSourcePrivateIps: Maybe; + + uniqueSourcePrivateIpsHistogram: Maybe; + + uniqueDestinationPrivateIps: Maybe; + + uniqueDestinationPrivateIpsHistogram: Maybe; + + dnsQueries: Maybe; + + tlsHandshakes: Maybe; + + inspect: Maybe; }; + export type UniqueSourcePrivateIpsHistogram = KpiNetworkChartFields.Fragment; + + export type UniqueDestinationPrivateIpsHistogram = KpiNetworkChartFields.Fragment; + export type Inspect = { __typename?: 'Inspect'; @@ -3832,11 +3988,15 @@ export namespace GetNetworkTopNFlowQuery { }; } -export namespace GetOverviewHostQuery { +export namespace GetTlsQuery { export type Variables = { sourceId: string; - timerange: TimerangeInput; filterQuery?: Maybe; + flowTarget: FlowTargetSourceDest; + ip: string; + pagination: PaginationInputPaginated; + sort: TlsSortField; + timerange: TimerangeInput; defaultIndex: string[]; inspect: boolean; }; @@ -3852,45 +4012,57 @@ export namespace GetOverviewHostQuery { id: string; - OverviewHost: Maybe; + Tls: Tls; }; - export type OverviewHost = { - __typename?: 'OverviewHostData'; + export type Tls = { + __typename?: 'TlsData'; - auditbeatAuditd: Maybe; + totalCount: number; - auditbeatFIM: Maybe; + edges: Edges[]; - auditbeatLogin: Maybe; + pageInfo: PageInfo; - auditbeatPackage: Maybe; + inspect: Maybe; + }; - auditbeatProcess: Maybe; + export type Edges = { + __typename?: 'TlsEdges'; - auditbeatUser: Maybe; + node: Node; - endgameDns: Maybe; + cursor: Cursor; + }; - endgameFile: Maybe; + export type Node = { + __typename?: 'TlsNode'; - endgameImageLoad: Maybe; + _id: Maybe; - endgameNetwork: Maybe; + subjects: Maybe; - endgameProcess: Maybe; + ja3: Maybe; - endgameRegistry: Maybe; + issuers: Maybe; - endgameSecurity: Maybe; + notAfter: Maybe; + }; - filebeatSystemModule: Maybe; + export type Cursor = { + __typename?: 'CursorType'; - winlogbeatSecurity: Maybe; + value: Maybe; + }; - winlogbeatMWSysmonOperational: Maybe; + export type PageInfo = { + __typename?: 'PageInfoPaginated'; - inspect: Maybe; + activePage: number; + + fakeTotalCount: number; + + showMorePagesIndicator: boolean; }; export type Inspect = { @@ -3902,11 +4074,15 @@ export namespace GetOverviewHostQuery { }; } -export namespace GetOverviewNetworkQuery { +export namespace GetUsersQuery { export type Variables = { sourceId: string; - timerange: TimerangeInput; filterQuery?: Maybe; + flowTarget: FlowTarget; + ip: string; + pagination: PaginationInputPaginated; + sort: UsersSortField; + timerange: TimerangeInput; defaultIndex: string[]; inspect: boolean; }; @@ -3922,35 +4098,67 @@ export namespace GetOverviewNetworkQuery { id: string; - OverviewNetwork: Maybe; + Users: Users; }; - export type OverviewNetwork = { - __typename?: 'OverviewNetworkData'; - - auditbeatSocket: Maybe; + export type Users = { + __typename?: 'UsersData'; - filebeatCisco: Maybe; + totalCount: number; - filebeatNetflow: Maybe; + edges: Edges[]; - filebeatPanw: Maybe; + pageInfo: PageInfo; - filebeatSuricata: Maybe; + inspect: Maybe; + }; - filebeatZeek: Maybe; + export type Edges = { + __typename?: 'UsersEdges'; - packetbeatDNS: Maybe; + node: Node; - packetbeatFlow: Maybe; + cursor: Cursor; + }; - packetbeatTLS: Maybe; + export type Node = { + __typename?: 'UsersNode'; - inspect: Maybe; + user: Maybe; }; - export type Inspect = { - __typename?: 'Inspect'; + export type User = { + __typename?: 'UsersItem'; + + name: Maybe; + + id: Maybe; + + groupId: Maybe; + + groupName: Maybe; + + count: Maybe; + }; + + export type Cursor = { + __typename?: 'CursorType'; + + value: Maybe; + }; + + export type PageInfo = { + __typename?: 'PageInfoPaginated'; + + activePage: number; + + fakeTotalCount: number; + + showMorePagesIndicator: boolean; + }; + + export type Inspect = { + __typename?: 'Inspect'; dsl: string[]; @@ -3958,10 +4166,13 @@ export namespace GetOverviewNetworkQuery { }; } -export namespace SourceQuery { +export namespace GetOverviewHostQuery { export type Variables = { - sourceId?: Maybe; + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; defaultIndex: string[]; + inspect: boolean; }; export type Query = { @@ -3975,37 +4186,109 @@ export namespace SourceQuery { id: string; - status: Status; + OverviewHost: Maybe; }; - export type Status = { - __typename?: 'SourceStatus'; + export type OverviewHost = { + __typename?: 'OverviewHostData'; - indicesExist: boolean; + auditbeatAuditd: Maybe; - indexFields: IndexFields[]; + auditbeatFIM: Maybe; + + auditbeatLogin: Maybe; + + auditbeatPackage: Maybe; + + auditbeatProcess: Maybe; + + auditbeatUser: Maybe; + + endgameDns: Maybe; + + endgameFile: Maybe; + + endgameImageLoad: Maybe; + + endgameNetwork: Maybe; + + endgameProcess: Maybe; + + endgameRegistry: Maybe; + + endgameSecurity: Maybe; + + filebeatSystemModule: Maybe; + + winlogbeatSecurity: Maybe; + + winlogbeatMWSysmonOperational: Maybe; + + inspect: Maybe; }; - export type IndexFields = { - __typename?: 'IndexField'; + export type Inspect = { + __typename?: 'Inspect'; - category: string; + dsl: string[]; - description: Maybe; + response: string[]; + }; +} - example: Maybe; +export namespace GetOverviewNetworkQuery { + export type Variables = { + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; + defaultIndex: string[]; + inspect: boolean; + }; - indexes: (Maybe)[]; + export type Query = { + __typename?: 'Query'; - name: string; + source: Source; + }; - searchable: boolean; + export type Source = { + __typename?: 'Source'; - type: string; + id: string; - aggregatable: boolean; + OverviewNetwork: Maybe; + }; - format: Maybe; + export type OverviewNetwork = { + __typename?: 'OverviewNetworkData'; + + auditbeatSocket: Maybe; + + filebeatCisco: Maybe; + + filebeatNetflow: Maybe; + + filebeatPanw: Maybe; + + filebeatSuricata: Maybe; + + filebeatZeek: Maybe; + + packetbeatDNS: Maybe; + + packetbeatFlow: Maybe; + + packetbeatTLS: Maybe; + + inspect: Maybe; + }; + + export type Inspect = { + __typename?: 'Inspect'; + + dsl: string[]; + + response: string[]; }; } @@ -5662,289 +5945,6 @@ export namespace PersistTimelinePinnedEventMutation { }; } -export namespace GetTlsQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - flowTarget: FlowTargetSourceDest; - ip: string; - pagination: PaginationInputPaginated; - sort: TlsSortField; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Tls: Tls; - }; - - export type Tls = { - __typename?: 'TlsData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'TlsEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'TlsNode'; - - _id: Maybe; - - subjects: Maybe; - - ja3: Maybe; - - issuers: Maybe; - - notAfter: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetUncommonProcessesQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - pagination: PaginationInputPaginated; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - UncommonProcesses: UncommonProcesses; - }; - - export type UncommonProcesses = { - __typename?: 'UncommonProcessesData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UncommonProcessesEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UncommonProcessItem'; - - _id: string; - - instances: number; - - process: Process; - - user: Maybe; - - hosts: Hosts[]; - }; - - export type Process = { - __typename?: 'ProcessEcsFields'; - - args: Maybe; - - name: Maybe; - }; - - export type User = { - __typename?: 'UserEcsFields'; - - id: Maybe; - - name: Maybe; - }; - - export type Hosts = { - __typename?: 'HostEcsFields'; - - name: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetUsersQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - flowTarget: FlowTarget; - ip: string; - pagination: PaginationInputPaginated; - sort: UsersSortField; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Users: Users; - }; - - export type Users = { - __typename?: 'UsersData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UsersEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UsersNode'; - - user: Maybe; - }; - - export type User = { - __typename?: 'UsersItem'; - - name: Maybe; - - id: Maybe; - - groupId: Maybe; - - groupName: Maybe; - - count: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - export namespace KpiHostDetailsChartFields { export type Fragment = { __typename?: 'KpiHostHistogramData'; diff --git a/x-pack/plugins/siem/scripts/generate_types_from_graphql.js b/x-pack/plugins/siem/scripts/generate_types_from_graphql.js index e6b063dfd2c07..21a37e31c6e80 100644 --- a/x-pack/plugins/siem/scripts/generate_types_from_graphql.js +++ b/x-pack/plugins/siem/scripts/generate_types_from_graphql.js @@ -11,7 +11,7 @@ const { join, resolve } = require('path'); const { generate } = require('graphql-code-generator'); const GRAPHQL_GLOBS = [ - join('public', 'containers', '**', '*.gql_query.ts{,x}'), + join('public', '**', '*.gql_query.ts{,x}'), join('common', 'graphql', '**', '*.gql_query.ts{,x}'), ]; const OUTPUT_INTROSPECTION_PATH = resolve('public', 'graphql', 'introspection.json'); From 09c950ba6b00accdce44229c0d10c0a1728019fc Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 15 May 2020 16:28:50 -0600 Subject: [PATCH 08/14] [Maps] Do not check count for blended layers when layer is not visible (#66460) * [Maps] Do not check count for blended layers when layer is not visible * move visibility logic to map_actions where syncData is called * clean up * fix syntax error * remove async action * make syncDataForAllLayers a proper redux action * simplify * fix functional test Co-authored-by: Elastic Machine --- .../maps/public/actions/map_actions.js | 81 ++++++++++--------- .../classes/layers/tile_layer/tile_layer.js | 3 - .../tiled_vector_layer/tiled_vector_layer.tsx | 4 - .../layers/vector_layer/vector_layer.js | 4 - .../vector_tile_layer/vector_tile_layer.js | 4 - .../es_archives/maps/kibana/data.json | 8 +- 6 files changed, 47 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/maps/public/actions/map_actions.js b/x-pack/plugins/maps/public/actions/map_actions.js index 5aa7c74a7b30b..1dfdfc3a73d8a 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.js +++ b/x-pack/plugins/maps/public/actions/map_actions.js @@ -111,14 +111,36 @@ function getLayerById(layerId, state) { }); } -async function syncDataForAllLayers(dispatch, getState, dataFilters) { - const state = getState(); - const layerList = getLayerList(state); - const syncs = layerList.map(layer => { - const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layer.getId()); - return layer.syncData({ ...loadingFunctions, dataFilters }); - }); - await Promise.all(syncs); +function syncDataForAllLayers() { + return async (dispatch, getState) => { + const syncPromises = getLayerList(getState()).map(async layer => { + return dispatch(syncDataForLayer(layer)); + }); + await Promise.all(syncPromises); + }; +} + +function syncDataForLayer(layer) { + return async (dispatch, getState) => { + const dataFilters = getDataFilters(getState()); + if (!layer.isVisible() || !layer.showAtZoomLevel(dataFilters.zoom)) { + return; + } + + await layer.syncData({ + ...getLayerLoadingCallbacks(dispatch, getState, layer.getId()), + dataFilters, + }); + }; +} + +function syncDataForLayerId(layerId) { + return async (dispatch, getState) => { + const layer = getLayerById(layerId, getState()); + if (layer) { + dispatch(syncDataForLayer(layer)); + } + }; } export function cancelAllInFlightRequests() { @@ -192,7 +214,7 @@ export function rollbackToTrackedLayerStateForSelectedLayer() { // syncDataForLayer may not trigger endDataLoad if no re-fetch is required dispatch(updateStyleMeta(layerId)); - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayerId(layerId)); }; } @@ -252,7 +274,7 @@ export function addLayer(layerDescriptor) { type: ADD_LAYER, layer: layerDescriptor, }); - dispatch(syncDataForLayer(layerDescriptor.id)); + dispatch(syncDataForLayerId(layerDescriptor.id)); }; } @@ -333,7 +355,7 @@ export function setLayerVisibility(layerId, makeVisible) { visibility: makeVisible, }); if (makeVisible) { - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayer(layer)); } }; } @@ -466,8 +488,7 @@ export function mapExtentChanged(newMapConstants) { ...newMapConstants, }, }); - const newDataFilters = { ...dataFilters, ...newMapConstants }; - await syncDataForAllLayers(dispatch, getState, newDataFilters); + await dispatch(syncDataForAllLayers()); }; } @@ -751,7 +772,7 @@ export function updateSourceProp(layerId, propName, value, newLayerType) { dispatch(updateLayerType(layerId, newLayerType)); } await dispatch(clearMissingStyleProperties(layerId)); - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayerId(layerId)); }; } @@ -771,20 +792,6 @@ function updateLayerType(layerId, newLayerType) { }; } -export function syncDataForLayer(layerId) { - return async (dispatch, getState) => { - const targetLayer = getLayerById(layerId, getState()); - if (targetLayer) { - const dataFilters = getDataFilters(getState()); - const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layerId); - await targetLayer.syncData({ - ...loadingFunctions, - dataFilters, - }); - } - }; -} - export function updateLayerLabel(id, newLabel) { return { type: UPDATE_LAYER_PROP, @@ -830,7 +837,7 @@ export function setLayerQuery(id, query) { newValue: query, }); - dispatch(syncDataForLayer(id)); + dispatch(syncDataForLayerId(id)); }; } @@ -895,8 +902,7 @@ export function setQuery({ query, timeFilters, filters = [], refresh = false }) filters, }); - const dataFilters = getDataFilters(getState()); - await syncDataForAllLayers(dispatch, getState, dataFilters); + await dispatch(syncDataForAllLayers()); }; } @@ -909,13 +915,12 @@ export function setRefreshConfig({ isPaused, interval }) { } export function triggerRefreshTimer() { - return async (dispatch, getState) => { + return async dispatch => { dispatch({ type: TRIGGER_REFRESH_TIMER, }); - const dataFilters = getDataFilters(getState()); - await syncDataForAllLayers(dispatch, getState, dataFilters); + await dispatch(syncDataForAllLayers()); }; } @@ -956,7 +961,7 @@ export function updateLayerStyle(layerId, styleDescriptor) { dispatch(updateStyleMeta(layerId)); // Style update may require re-fetch, for example ES search may need to retrieve field used for dynamic styling - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayerId(layerId)); }; } @@ -994,12 +999,12 @@ export function setJoinsForLayer(layer, joins) { return async dispatch => { await dispatch({ type: SET_JOINS, - layer: layer, - joins: joins, + layer, + joins, }); await dispatch(clearMissingStyleProperties(layer.getId())); - dispatch(syncDataForLayer(layer.getId())); + dispatch(syncDataForLayerId(layer.getId())); }; } diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js index 69f5033e3af0f..5108e5cd3e6a3 100644 --- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js @@ -25,9 +25,6 @@ export class TileLayer extends AbstractLayer { } async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) { - if (!this.isVisible() || !this.showAtZoomLevel(dataFilters.zoom)) { - return; - } const sourceDataRequest = this.getSourceDataRequest(); if (sourceDataRequest) { //data is immmutable diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx index bb4fbe9d01b60..f8f78f6e97829 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx @@ -78,10 +78,6 @@ export class TiledVectorLayer extends VectorLayer { } async syncData(syncContext: SyncContext) { - if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { - return; - } - await this._syncSourceStyleMeta(syncContext, this._source, this._style); await this._syncSourceFormatters(syncContext, this._source, this._style); await this._syncMVTUrlTemplate(syncContext); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js index ccbc8a1c21324..d32593f73c46c 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js @@ -571,10 +571,6 @@ export class VectorLayer extends AbstractLayer { // Given 2 above, which source/style to use can not be pulled from data request state. // Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle. async _syncData(syncContext, source, style) { - if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { - return; - } - await this._syncSourceStyleMeta(syncContext, source, style); await this._syncSourceFormatters(syncContext, source, style); const sourceResult = await this._syncSource(syncContext, source, style); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js index fe1ff58922162..4ffd0d93fd22a 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js @@ -45,10 +45,6 @@ export class VectorTileLayer extends TileLayer { } async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) { - if (!this.isVisible() || !this.showAtZoomLevel(dataFilters.zoom)) { - return; - } - const nextMeta = { tileLayerId: this.getSource().getTileLayerId() }; const canSkipSync = this._canSkipSync({ prevDataRequest: this.getSourceDataRequest(), diff --git a/x-pack/test/functional/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json index cb3598652a39a..d313fd2046c03 100644 --- a/x-pack/test/functional/es_archives/maps/kibana/data.json +++ b/x-pack/test/functional/es_archives/maps/kibana/data.json @@ -233,7 +233,7 @@ "title" : "document example top hits", "description" : "", "mapStateJSON" : "{\"zoom\":4.1,\"center\":{\"lon\":-100.61091,\"lat\":33.23887},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-24T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000},\"query\":{\"query\":\"\",\"language\":\"kuery\"}}", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"z52lq\",\"label\":\"logstash\",\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[],\"topHitsTimeField\":\"@timestamp\",\"useTopHits\":true,\"topHitsSplitField\":\"machine.os.raw\",\"topHitsSize\":2,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"previousStyle\":null},\"type\":\"VECTOR\"}]", + "layerListJSON" : "[{\"id\":\"z52lq\",\"label\":\"logstash\",\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[],\"topHitsTimeField\":\"@timestamp\",\"useTopHits\":true,\"topHitsSplitField\":\"machine.os.raw\",\"topHitsSize\":2,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"previousStyle\":null},\"type\":\"VECTOR\"}]", "uiStateJSON" : "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}", "bounds" : { "type" : "polygon", @@ -467,7 +467,7 @@ "type": "envelope" }, "description": "", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"label\":\"EMS base layer (road_map)\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[\"name\"],\"applyGlobalQuery\":false,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1_groupby_meta_for_geo_shapes*.shape_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]}]", + "layerListJSON" : "[{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[\"name\"],\"applyGlobalQuery\":false,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1_groupby_meta_for_geo_shapes*.shape_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]}]", "mapStateJSON": "{\"zoom\":3.02,\"center\":{\"lon\":77.33426,\"lat\":-0.04647},\"timeFilters\":{\"from\":\"now-17m\",\"to\":\"now\",\"mode\":\"quick\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}", "title": "join example", "uiStateJSON": "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[\"n1t6f\"]}" @@ -512,7 +512,7 @@ ], "type": "envelope" }, - "layerListJSON": "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"3xlvm\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"427aa49d-a552-4e7d-a629-67c47db27128\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"heatmap\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"HEATMAP\",\"refinement\":\"coarse\",\"properties\":{},\"previousStyle\":null},\"type\":\"HEATMAP\"}]", + "layerListJSON": "[{\"id\":\"3xlvm\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"427aa49d-a552-4e7d-a629-67c47db27128\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"heatmap\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"HEATMAP\",\"refinement\":\"coarse\",\"properties\":{},\"previousStyle\":null},\"type\":\"HEATMAP\"}]", "mapStateJSON": "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}", "title": "geo grid heatmap example", "uiStateJSON": "{\"isDarkMode\":false}" @@ -543,7 +543,7 @@ "type": "envelope" }, "description": "", - "layerListJSON": "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"g1xkv\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"9305f6ea-4518-4c06-95b9-33321aa38d6a\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"max\",\"field\":\"bytes\"}]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max of bytes\",\"name\":\"max_of_bytes\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\"}]", + "layerListJSON": "[{\"id\":\"g1xkv\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"9305f6ea-4518-4c06-95b9-33321aa38d6a\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"max\",\"field\":\"bytes\"}]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max of bytes\",\"name\":\"max_of_bytes\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\"}]", "mapStateJSON": "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}", "title": "geo grid vector grid example", "uiStateJSON": "{\"isDarkMode\":false}" From ac0946069b95faeeb881cdfc2e1e1456f7e7df43 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Fri, 15 May 2020 17:34:55 -0500 Subject: [PATCH 09/14] [Uptime] Fix flaky navigation to certs page in tests (#66806) Fix flaky navigation to certs page in tests. Fixes https://github.com/elastic/kibana/issues/66778 --- x-pack/test/functional/services/uptime/navigation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/services/uptime/navigation.ts b/x-pack/test/functional/services/uptime/navigation.ts index 37cc71d6865b0..d372bd53c081b 100644 --- a/x-pack/test/functional/services/uptime/navigation.ts +++ b/x-pack/test/functional/services/uptime/navigation.ts @@ -65,8 +65,8 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv }, goToCertificates: async () => { - await testSubjects.click('uptimeCertificatesLink', 10000); - return retry.tryForTime(60 * 1000, async () => { + return retry.try(async () => { + await testSubjects.click('uptimeCertificatesLink'); await testSubjects.existOrFail('uptimeCertificatesPage'); }); }, From bfdeb10c15027a3784c733e80641348a0d20e429 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 15 May 2020 16:11:27 -0700 Subject: [PATCH 10/14] Reorganize Management apps into Ingest, Data, Alerts and Insights, Security, Kibana, and Stack groups (#65796) --- .../sections/index_patterns/index.js | 3 +- .../advanced_settings/public/plugin.ts | 9 +-- .../public/search/long_query_notification.tsx | 4 +- .../management_sidebar_nav/_sidebar_nav.scss | 2 +- .../management_sidebar_nav.tsx | 4 +- src/plugins/management/public/index.ts | 1 + .../public/legacy/sections_register.js | 31 ++------ .../public/management_section.test.ts | 3 +- .../management/public/management_section.ts | 8 +- .../management/public/management_sections.tsx | 77 +++++++++++++++++++ .../public/management_service.test.ts | 31 ++------ .../management/public/management_service.ts | 31 +++++--- src/plugins/management/public/mocks/index.ts | 1 - src/plugins/management/public/types.ts | 38 +++++++-- .../saved_objects_management/public/plugin.ts | 9 +-- .../management_test_plugin/public/plugin.tsx | 15 ++-- .../management/management_plugin.js | 2 +- .../common/constants/index.ts | 2 +- .../AlertIntegrations/index.tsx | 2 +- .../ServiceIntegrations/WatcherFlyout.tsx | 2 +- .../ServiceIntegrations/index.tsx | 2 +- .../app/ServiceOverview/NoServicesMessage.tsx | 2 +- .../NoServicesMessage.test.tsx.snap | 4 +- .../ServiceOverview.test.tsx.snap | 2 +- .../components/app/ServiceOverview/index.tsx | 2 +- .../components/shared/LicensePrompt/index.tsx | 2 +- .../InvalidLicenseNotification.tsx | 2 +- x-pack/plugins/apm/public/setHelpExtension.ts | 2 +- .../beats_management/public/application.tsx | 7 +- .../beats_management/public/bootstrap.tsx | 30 ++------ .../lib/adapters/framework/adapter_types.ts | 14 +--- .../framework/kibana_framework_adapter.ts | 43 +++-------- .../framework/testing_framework_adapter.ts | 9 --- .../beats_management/public/lib/framework.ts | 1 - .../common/constants/index.ts | 4 +- .../public/plugin.ts | 5 +- .../extend_index_management.test.js.snap | 8 +- .../__snapshots__/policy_table.test.js.snap | 2 +- .../common/constants/index.ts | 2 +- .../public/plugin.tsx | 5 +- .../common/constants/base_path.ts | 2 +- .../public/application/services/navigation.ts | 4 +- .../plugins/index_management/public/plugin.ts | 6 +- .../components/alert_dropdown.tsx | 2 +- .../alerting/inventory/alert_dropdown.tsx | 2 +- .../alerting/logs/alert_dropdown.tsx | 2 +- .../ingest_pipelines/common/constants.ts | 2 +- .../plugins/ingest_pipelines/public/plugin.ts | 3 +- .../__snapshots__/add_license.test.js.snap | 4 +- .../upload_license.test.tsx.snap | 20 ++--- .../common/constants/base_path.ts | 2 +- .../license_management/public/plugin.ts | 6 +- .../plugins/licensing/public/plugin.test.ts | 2 +- x-pack/plugins/licensing/public/plugin.ts | 2 +- .../public/application/breadcrumbs.js | 2 +- x-pack/plugins/logstash/public/plugin.ts | 39 +++++----- .../datavisualizer_selector.tsx | 2 +- .../results_links/results_links.tsx | 2 +- .../create_watch_service.js | 2 +- .../ml/public/application/management/index.ts | 23 +++--- .../overview/components/sidebar.tsx | 2 +- .../components/cluster/listing/listing.js | 2 +- .../public/components/license/index.js | 2 +- .../public/application/constants/paths.ts | 2 +- .../plugins/remote_clusters/public/plugin.ts | 7 +- x-pack/plugins/reporting/public/plugin.tsx | 6 +- .../rollup/public/crud_app/constants/paths.js | 2 +- x-pack/plugins/rollup/public/plugin.ts | 37 ++++----- .../management/management_service.test.ts | 14 +--- .../public/management/management_service.ts | 13 +--- .../rules/select_rule_type/index.tsx | 2 +- .../upgrade_contents.test.tsx.snap | 2 +- .../ml_popover/upgrade_contents.tsx | 2 +- .../public/application/constants/index.ts | 2 +- .../plugins/snapshot_restore/public/plugin.ts | 6 +- .../management/management_service.test.ts | 2 +- .../public/management/management_service.tsx | 13 ++-- .../management/spaces_management_app.test.tsx | 2 +- .../management/spaces_management_app.tsx | 2 +- x-pack/plugins/spaces/public/plugin.test.ts | 4 +- .../transform/public/app/constants/index.ts | 2 +- x-pack/plugins/transform/public/plugin.ts | 28 ++++--- .../transform/public/register_feature.ts | 2 +- .../translations/translations/ja-JP.json | 5 -- .../translations/translations/zh-CN.json | 5 -- .../public/application/constants/index.ts | 2 +- .../connector_add_flyout.test.tsx | 2 +- .../triggers_actions_ui/public/plugin.ts | 6 +- .../upgrade_assistant/public/plugin.ts | 6 +- .../__snapshots__/license_info.test.tsx.snap | 4 +- .../__snapshots__/ml_flyout.test.tsx.snap | 2 +- .../components/monitor/ml/license_info.tsx | 2 +- .../alerts/toggle_alert_flyout_button.tsx | 2 +- .../client_integration/watch_list.test.ts | 2 +- .../watcher/public/application/app.tsx | 2 +- .../public/application/constants/base_path.ts | 2 +- .../public/application/lib/breadcrumbs.ts | 2 +- .../monitoring_watch_edit.tsx | 4 +- .../watch_list/components/watch_list.tsx | 4 +- x-pack/plugins/watcher/public/plugin.ts | 8 +- .../report_delete_pagination.ts | 2 +- x-pack/test/functional/config.js | 24 +++--- x-pack/test/functional_with_es_ssl/config.ts | 2 +- 103 files changed, 391 insertions(+), 405 deletions(-) create mode 100644 src/plugins/management/public/management_sections.tsx diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js index a8376c0e84bf9..24bc4ba8fba5b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js @@ -18,6 +18,7 @@ */ import { management } from 'ui/management'; +import { ManagementSectionId } from '../../../../../../../plugins/management/public'; import './create_index_pattern_wizard'; import './edit_index_pattern'; import uiRoutes from 'ui/routes'; @@ -163,7 +164,7 @@ uiModules }; }); -management.getSection('kibana').register('index_patterns', { +management.getSection(ManagementSectionId.Kibana).register('index_patterns', { display: i18n.translate('kbn.management.indexPattern.sectionsHeader', { defaultMessage: 'Index Patterns', }), diff --git a/src/plugins/advanced_settings/public/plugin.ts b/src/plugins/advanced_settings/public/plugin.ts index 04eeff1e1f3ce..2784b74ab726c 100644 --- a/src/plugins/advanced_settings/public/plugin.ts +++ b/src/plugins/advanced_settings/public/plugin.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { ManagementApp } from '../../management/public'; +import { ManagementApp, ManagementSectionId } from '../../management/public'; import { ComponentRegistry } from './component_registry'; import { AdvancedSettingsSetup, AdvancedSettingsStart, AdvancedSettingsPluginSetup } from './types'; @@ -32,15 +32,12 @@ export class AdvancedSettingsPlugin implements Plugin { private managementApp?: ManagementApp; public setup(core: CoreSetup, { management }: AdvancedSettingsPluginSetup) { - const kibanaSection = management.sections.getSection('kibana'); - if (!kibanaSection) { - throw new Error('`kibana` management section not found.'); - } + const kibanaSection = management.sections.getSection(ManagementSectionId.Kibana); this.managementApp = kibanaSection.registerApp({ id: 'settings', title, - order: 20, + order: 3, async mount(params) { const { mountManagementSection } = await import( './management_app/mount_management_section' diff --git a/src/plugins/data/public/search/long_query_notification.tsx b/src/plugins/data/public/search/long_query_notification.tsx index 590fee20db690..0bdf8ab7c66f8 100644 --- a/src/plugins/data/public/search/long_query_notification.tsx +++ b/src/plugins/data/public/search/long_query_notification.tsx @@ -44,9 +44,7 @@ export function LongQueryNotification(props: Props) { { - await props.application.navigateToApp( - 'kibana#/management/elasticsearch/license_management' - ); + await props.application.navigateToApp('kibana#/management/stack/license_management'); }} > { + this.main.register(id, { + display: title, + order: idx, + }); }); return this.main; diff --git a/src/plugins/management/public/management_section.test.ts b/src/plugins/management/public/management_section.test.ts index c68175ee0a678..e1d047425ac18 100644 --- a/src/plugins/management/public/management_section.test.ts +++ b/src/plugins/management/public/management_section.test.ts @@ -18,6 +18,7 @@ */ import { ManagementSection } from './management_section'; +import { ManagementSectionId } from './types'; // @ts-ignore import { LegacyManagementSection } from './legacy'; import { coreMock } from '../../../core/public/mocks'; @@ -27,7 +28,7 @@ function createSection(registerLegacyApp: () => void) { const getLegacySection = () => legacySection; const getManagementSections: () => ManagementSection[] = () => []; - const testSectionConfig = { id: 'test-section', title: 'Test Section' }; + const testSectionConfig = { id: ManagementSectionId.Data, title: 'Test Section' }; return new ManagementSection( testSectionConfig, getManagementSections, diff --git a/src/plugins/management/public/management_section.ts b/src/plugins/management/public/management_section.ts index 483605341ae4c..ace8f87bec766 100644 --- a/src/plugins/management/public/management_section.ts +++ b/src/plugins/management/public/management_section.ts @@ -17,7 +17,9 @@ * under the License. */ -import { CreateSection, RegisterManagementAppArgs } from './types'; +import { ReactElement } from 'react'; + +import { CreateSection, RegisterManagementAppArgs, ManagementSectionId } from './types'; import { KibanaLegacySetup } from '../../kibana_legacy/public'; import { StartServicesAccessor } from '../../../core/public'; // @ts-ignore @@ -25,8 +27,8 @@ import { LegacyManagementSection } from './legacy'; import { ManagementApp } from './management_app'; export class ManagementSection { - public readonly id: string = ''; - public readonly title: string = ''; + public readonly id: ManagementSectionId; + public readonly title: string | ReactElement = ''; public readonly apps: ManagementApp[] = []; public readonly order: number; public readonly euiIconType?: string; diff --git a/src/plugins/management/public/management_sections.tsx b/src/plugins/management/public/management_sections.tsx new file mode 100644 index 0000000000000..77e494626a00e --- /dev/null +++ b/src/plugins/management/public/management_sections.tsx @@ -0,0 +1,77 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiIcon } from '@elastic/eui'; + +import { ManagementSectionId } from './types'; + +interface Props { + text: string; + tip: string; +} + +const ManagementSectionTitle = ({ text, tip }: Props) => ( + + + {text} + + + + + + +); + +export const managementSections = [ + { + id: ManagementSectionId.Ingest, + title: ( + + ), + }, + { + id: ManagementSectionId.Data, + title: , + }, + { + id: ManagementSectionId.InsightsAndAlerting, + title: ( + + ), + }, + { + id: ManagementSectionId.Security, + title: , + }, + { + id: ManagementSectionId.Kibana, + title: , + }, + { + id: ManagementSectionId.Stack, + title: , + }, +]; diff --git a/src/plugins/management/public/management_service.test.ts b/src/plugins/management/public/management_service.test.ts index 18569ef285ff3..1507d6f43619d 100644 --- a/src/plugins/management/public/management_service.test.ts +++ b/src/plugins/management/public/management_service.test.ts @@ -18,6 +18,7 @@ */ import { ManagementService } from './management_service'; +import { ManagementSectionId } from './types'; import { coreMock } from '../../../core/public/mocks'; import { npSetup } from '../../../legacy/ui/public/new_platform/__mocks__'; @@ -29,27 +30,11 @@ test('Provides default sections', () => { () => {}, coreMock.createSetup().getStartServices ); - expect(service.getAllSections().length).toEqual(2); - expect(service.getSection('kibana')).not.toBeUndefined(); - expect(service.getSection('elasticsearch')).not.toBeUndefined(); -}); - -test('Register section, enable and disable', () => { - const service = new ManagementService().setup( - npSetup.plugins.kibanaLegacy, - () => {}, - coreMock.createSetup().getStartServices - ); - const testSection = service.register({ id: 'test-section', title: 'Test Section' }); - expect(service.getSection('test-section')).not.toBeUndefined(); - - const testApp = testSection.registerApp({ - id: 'test-app', - title: 'Test App', - mount: () => () => {}, - }); - expect(testSection.getApp('test-app')).not.toBeUndefined(); - expect(service.getSectionsEnabled().length).toEqual(1); - testApp.disable(); - expect(service.getSectionsEnabled().length).toEqual(0); + expect(service.getAllSections().length).toEqual(6); + expect(service.getSection(ManagementSectionId.Ingest)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Data)).toBeDefined(); + expect(service.getSection(ManagementSectionId.InsightsAndAlerting)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Security)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Kibana)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Stack)).toBeDefined(); }); diff --git a/src/plugins/management/public/management_service.ts b/src/plugins/management/public/management_service.ts index 8fc207e32e6ce..85d27a526d402 100644 --- a/src/plugins/management/public/management_service.ts +++ b/src/plugins/management/public/management_service.ts @@ -17,11 +17,14 @@ * under the License. */ +import { ReactElement } from 'react'; + import { ManagementSection } from './management_section'; +import { managementSections } from './management_sections'; import { KibanaLegacySetup } from '../../kibana_legacy/public'; // @ts-ignore -import { LegacyManagementSection } from './legacy'; -import { CreateSection } from './types'; +import { LegacyManagementSection, sections } from './legacy'; +import { CreateSection, ManagementSectionId } from './types'; import { StartServicesAccessor, CoreStart } from '../../../core/public'; export class ManagementService { @@ -48,7 +51,8 @@ export class ManagementService { return newSection; }; } - private getSection(sectionId: ManagementSection['id']) { + + private getSection(sectionId: ManagementSectionId) { return this.sections.find(section => section.id === sectionId); } @@ -63,7 +67,13 @@ export class ManagementService { } private sharedInterface = { - getSection: this.getSection.bind(this), + getSection: (sectionId: ManagementSectionId) => { + const section = this.getSection(sectionId); + if (!section) { + throw new Error(`Management section with id ${sectionId} is undefined`); + } + return section; + }, getSectionsEnabled: this.getSectionsEnabled.bind(this), getAllSections: this.getAllSections.bind(this), }; @@ -79,16 +89,13 @@ export class ManagementService { getStartServices ); - register({ id: 'kibana', title: 'Kibana', order: 30, euiIconType: 'logoKibana' }); - register({ - id: 'elasticsearch', - title: 'Elasticsearch', - order: 20, - euiIconType: 'logoElasticsearch', - }); + managementSections.forEach( + ({ id, title }: { id: ManagementSectionId; title: ReactElement }, idx: number) => { + register({ id, title, order: idx }); + } + ); return { - register, ...this.sharedInterface, }; } diff --git a/src/plugins/management/public/mocks/index.ts b/src/plugins/management/public/mocks/index.ts index 82789d3c3f55f..3e32ff4fe26b2 100644 --- a/src/plugins/management/public/mocks/index.ts +++ b/src/plugins/management/public/mocks/index.ts @@ -30,7 +30,6 @@ const createManagementSectionMock = (): jest.Mocked => ({ sections: { - register: jest.fn(), getSection: jest.fn().mockReturnValue(createManagementSectionMock()), getAllSections: jest.fn().mockReturnValue([]), }, diff --git a/src/plugins/management/public/types.ts b/src/plugins/management/public/types.ts index a8bdd5cca24a3..ecd727e8703ff 100644 --- a/src/plugins/management/public/types.ts +++ b/src/plugins/management/public/types.ts @@ -17,6 +17,26 @@ * under the License. */ +/* + * 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 { ReactElement } from 'react'; import { IconType } from '@elastic/eui'; import { ManagementApp } from './management_app'; import { ManagementSection } from './management_section'; @@ -31,21 +51,29 @@ export interface ManagementStart { legacy: any; } +export enum ManagementSectionId { + Ingest = 'ingest', + Data = 'data', + InsightsAndAlerting = 'insightsAndAlerting', + Security = 'security', + Kibana = 'kibana', + Stack = 'stack', +} + interface SectionsServiceSetup { - getSection: (sectionId: ManagementSection['id']) => ManagementSection | undefined; + getSection: (sectionId: ManagementSectionId) => ManagementSection; getAllSections: () => ManagementSection[]; - register: RegisterSection; } interface SectionsServiceStart { - getSection: (sectionId: ManagementSection['id']) => ManagementSection | undefined; + getSection: (sectionId: ManagementSectionId) => ManagementSection; getAllSections: () => ManagementSection[]; navigateToApp: ApplicationStart['navigateToApp']; } export interface CreateSection { - id: string; - title: string; + id: ManagementSectionId; + title: string | ReactElement; order?: number; euiIconType?: string; // takes precedence over `icon` property. icon?: string; // URL to image file; fallback if no `euiIconType` diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index 28eac96dcbf46..b0c6b1952a2a5 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { ManagementSetup } from '../../management/public'; +import { ManagementSetup, ManagementSectionId } from '../../management/public'; import { DataPublicPluginStart } from '../../data/public'; import { DashboardStart } from '../../dashboard/public'; import { DiscoverStart } from '../../discover/public'; @@ -87,16 +87,13 @@ export class SavedObjectsManagementPlugin category: FeatureCatalogueCategory.ADMIN, }); - const kibanaSection = management.sections.getSection('kibana'); - if (!kibanaSection) { - throw new Error('`kibana` management section not found.'); - } + const kibanaSection = management.sections.getSection(ManagementSectionId.Kibana); kibanaSection.registerApp({ id: 'objects', title: i18n.translate('savedObjectsManagement.managementSectionLabel', { defaultMessage: 'Saved Objects', }), - order: 10, + order: 1, mount: async mountParams => { const { mountManagementSection } = await import('./management_section'); return mountManagementSection({ diff --git a/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx b/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx index f3b7a19f70ae3..96297f6d51566 100644 --- a/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx +++ b/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx @@ -21,22 +21,17 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { HashRouter as Router, Switch, Route, Link } from 'react-router-dom'; import { CoreSetup, Plugin } from 'kibana/public'; -import { ManagementSetup } from '../../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../../src/plugins/management/public'; export class ManagementTestPlugin implements Plugin { public setup(core: CoreSetup, { management }: { management: ManagementSetup }) { - const testSection = management.sections.register({ - id: 'test-section', - title: 'Test Section', - euiIconType: 'logoKibana', - order: 25, - }); + const testSection = management.sections.getSection(ManagementSectionId.Data); - testSection!.registerApp({ + testSection.registerApp({ id: 'test-management', title: 'Management Test', - mount(params) { + mount(params: any) { params.setBreadcrumbs([{ text: 'Management Test' }]); ReactDOM.render( @@ -63,7 +58,7 @@ export class ManagementTestPlugin }, }); - testSection! + testSection .registerApp({ id: 'test-management-disabled', title: 'Management Test Disabled', diff --git a/test/plugin_functional/test_suites/management/management_plugin.js b/test/plugin_functional/test_suites/management/management_plugin.js index 0c185f4b385b5..6ad2bb56391dd 100644 --- a/test/plugin_functional/test_suites/management/management_plugin.js +++ b/test/plugin_functional/test_suites/management/management_plugin.js @@ -40,7 +40,7 @@ export default function({ getService, getPageObjects }) { it('should redirect when app is disabled', async () => { await PageObjects.common.navigateToActualUrl( 'kibana', - 'management/test-section/test-management-disabled' + 'management/data/test-management-disabled' ); await testSubjects.existOrFail('management-landing'); }); diff --git a/x-pack/legacy/plugins/beats_management/common/constants/index.ts b/x-pack/legacy/plugins/beats_management/common/constants/index.ts index 5f9ae815e34d4..8d22b36e96d45 100644 --- a/x-pack/legacy/plugins/beats_management/common/constants/index.ts +++ b/x-pack/legacy/plugins/beats_management/common/constants/index.ts @@ -9,4 +9,4 @@ export { INDEX_NAMES } from './index_names'; export { PLUGIN } from './plugin'; export { LICENSES, REQUIRED_LICENSES, REQUIRED_ROLES } from './security'; export { TABLE_CONFIG } from './table'; -export const BASE_PATH = '/management/beats/beats_management'; +export const BASE_PATH = '/management/ingest/beats_management'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx index 75c6c79bc804a..9001eb6992a96 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx @@ -82,7 +82,7 @@ export function AlertIntegrations(props: Props) { } ), href: plugin.core.http.basePath.prepend( - '/app/kibana#/management/kibana/triggersActions/alerts' + '/app/kibana#/management/insightsAndAlerting/triggersActions/alerts' ), icon: 'tableOfContents' } diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx index 3bbd8a01d0549..c0c93bb4cb298 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx @@ -258,7 +258,7 @@ export class WatcherFlyout extends Component< )}{' '} {i18n.translate( 'xpack.apm.serviceDetails.enableErrorReportsPanel.watchCreatedNotificationText.viewWatchLinkText', diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx index 91483b4b52d90..e1f58b7b35210 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx @@ -92,7 +92,7 @@ export class ServiceIntegrations extends React.Component { ), icon: 'watchesApp', href: core.http.basePath.prepend( - '/app/kibana#/management/elasticsearch/watcher' + '/app/kibana#/management/insightsAndAlerting/watcher' ), target: '_blank', onClick: () => this.closePopover() diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx index c1afa433cb614..266e5a97ef07a 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx @@ -66,7 +66,7 @@ export function NoServicesMessage({ historicalDataFound, status }: Props) { defaultMessage: 'You may also have old data that needs to be migrated.' })}{' '} - + {i18n.translate('xpack.apm.servicesTable.UpgradeAssistantLink', { defaultMessage: 'Learn more by visiting the Kibana Upgrade Assistant' diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap index 227becb9a9c4f..d027422961c99 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap @@ -25,7 +25,7 @@ exports[`NoServicesMessage status: pending and historicalDataFound: false 1`] = You may also have old data that needs to be migrated. Learn more by visiting the Kibana Upgrade Assistant @@ -70,7 +70,7 @@ exports[`NoServicesMessage status: success and historicalDataFound: false 1`] = You may also have old data that needs to be migrated. Learn more by visiting the Kibana Upgrade Assistant diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap index 6d310199ba9a5..3e6be107ce3a1 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap @@ -79,7 +79,7 @@ NodeList [ Learn more by visiting the Kibana Upgrade Assistant diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx index 99b169e3ec361..06f56d9ec1be7 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx @@ -66,7 +66,7 @@ export function ServiceOverview() { {i18n.translate( diff --git a/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx b/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx index d2afefb83a568..96e8c754fcc5f 100644 --- a/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx @@ -17,7 +17,7 @@ interface Props { export const LicensePrompt = ({ text, showBetaBadge = false }: Props) => { const licensePageUrl = useKibanaUrl( '/app/kibana', - '/management/elasticsearch/license_management/home' + '/management/stack/license_management/home' ); const renderLicenseBody = ( diff --git a/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx b/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx index 36e780f50c3ae..8ed02f039289e 100644 --- a/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx +++ b/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx @@ -11,7 +11,7 @@ import { useApmPluginContext } from '../../hooks/useApmPluginContext'; export function InvalidLicenseNotification() { const { core } = useApmPluginContext(); const manageLicenseURL = core.http.basePath.prepend( - '/app/kibana#/management/elasticsearch/license_management' + '/app/kibana#/management/stack/license_management' ); return ( diff --git a/x-pack/plugins/apm/public/setHelpExtension.ts b/x-pack/plugins/apm/public/setHelpExtension.ts index 1a3394651b2ff..aa23a8a2e64ea 100644 --- a/x-pack/plugins/apm/public/setHelpExtension.ts +++ b/x-pack/plugins/apm/public/setHelpExtension.ts @@ -22,7 +22,7 @@ export function setHelpExtension({ chrome, http }: CoreStart) { linkType: 'custom', href: url.format({ pathname: http.basePath.prepend('/app/kibana'), - hash: '/management/elasticsearch/upgrade_assistant' + hash: '/management/stack/upgrade_assistant' }), content: i18n.translate('xpack.apm.helpMenu.upgradeAssistantLink', { defaultMessage: 'Upgrade assistant' diff --git a/x-pack/plugins/beats_management/public/application.tsx b/x-pack/plugins/beats_management/public/application.tsx index bf450e9c7a5e3..6711e93895b62 100644 --- a/x-pack/plugins/beats_management/public/application.tsx +++ b/x-pack/plugins/beats_management/public/application.tsx @@ -19,7 +19,10 @@ import { TagsContainer } from './containers/tags'; import { FrontendLibs } from './lib/types'; import { AppRouter } from './router'; import { services } from './kbn_services'; -import { ManagementAppMountParams } from '../../../../src/plugins/management/public'; +import { + ManagementAppMountParams, + ManagementSectionId, +} from '../../../../src/plugins/management/public'; export const renderApp = ( { basePath, element, setBreadcrumbs }: ManagementAppMountParams, @@ -28,7 +31,7 @@ export const renderApp = ( ReactDOM.render( - + diff --git a/x-pack/plugins/beats_management/public/bootstrap.tsx b/x-pack/plugins/beats_management/public/bootstrap.tsx index ecca9da052fea..9a45be702e212 100644 --- a/x-pack/plugins/beats_management/public/bootstrap.tsx +++ b/x-pack/plugins/beats_management/public/bootstrap.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { i18n } from '@kbn/i18n'; import { FrontendLibs } from './lib/types'; import { compose } from './lib/compose/kibana'; @@ -20,27 +19,14 @@ async function startApp(libs: FrontendLibs, core: CoreSetup) { await libs.framework.waitUntilFrameworkReady(); if (libs.framework.licenseIsAtLeast('standard')) { - libs.framework.registerManagementSection({ - id: 'beats', - name: i18n.translate('xpack.beatsManagement.centralManagementSectionLabel', { - defaultMessage: 'Beats', - }), - iconName: 'logoBeats', - }); - - libs.framework.registerManagementUI({ - sectionId: 'beats', - appId: 'beats_management', - name: i18n.translate('xpack.beatsManagement.centralManagementLinkLabel', { - defaultMessage: 'Central Management', - }), - async mount(params) { - const [coreStart, pluginsStart] = await core.getStartServices(); - setServices(coreStart, pluginsStart, params); - const { renderApp } = await import('./application'); - return renderApp(params, libs); - }, - }); + const mount = async (params: any) => { + const [coreStart, pluginsStart] = await core.getStartServices(); + setServices(coreStart, pluginsStart, params); + const { renderApp } = await import('./application'); + return renderApp(params, libs); + }; + + libs.framework.registerManagementUI(mount); } } diff --git a/x-pack/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts index 3c0c724d3d87b..9d7a1954b60d9 100644 --- a/x-pack/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts @@ -17,19 +17,7 @@ export interface FrameworkAdapter { currentUser: FrameworkUser; // Methods waitUntilFrameworkReady(): Promise; - registerManagementSection(settings: { - id: string; - name: string; - iconName: string; - order?: number; - }): void; - registerManagementUI(settings: { - sectionId: string; - appId: string; - name: string; - order?: number; - mount: RegisterManagementAppArgs['mount']; - }): void; + registerManagementUI(mount: RegisterManagementAppArgs['mount']): void; } export const RuntimeFrameworkInfo = t.type({ diff --git a/x-pack/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts index d061e2f35809b..1ae21a561950d 100644 --- a/x-pack/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts @@ -9,6 +9,7 @@ import { IScope } from 'angular'; import { PathReporter } from 'io-ts/lib/PathReporter'; import { isLeft } from 'fp-ts/lib/Either'; import { first } from 'rxjs/operators'; +import { i18n } from '@kbn/i18n'; import { SecurityPluginSetup } from '../../../../../security/public'; import { BufferedKibanaServiceCall, KibanaAdapterServiceRefs, KibanaUIConfig } from '../../types'; import { @@ -21,6 +22,7 @@ import { import { ManagementSetup, RegisterManagementAppArgs, + ManagementSectionId, } from '../../../../../../../src/plugins/management/public'; import { LicensingPluginSetup } from '../../../../../licensing/public'; import { BeatsManagementConfigType } from '../../../../common'; @@ -102,40 +104,15 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter { } } - public registerManagementSection(settings: { - id: string; - name: string; - iconName: string; - order?: number; - }) { - this.management.sections.register({ - id: settings.id, - title: settings.name, - euiIconType: settings.iconName, - order: settings.order || 30, - }); - } - - public registerManagementUI(settings: { - sectionId: string; - appId: string; - name: string; - order?: number; - mount: RegisterManagementAppArgs['mount']; - }) { - const section = this.management.sections.getSection(settings.sectionId); - - if (!section) { - throw new Error( - `registerManagementUI was called with a sectionId of ${settings.sectionId}, and that is is not yet regestered as a section` - ); - } - + public registerManagementUI(mount: RegisterManagementAppArgs['mount']) { + const section = this.management.sections.getSection(ManagementSectionId.Ingest); section.registerApp({ - id: settings.appId, - title: settings.name, - order: settings.order || 30, - mount: settings.mount, + id: 'beats_management', + title: i18n.translate('xpack.beatsManagement.centralManagementLinkLabel', { + defaultMessage: 'Beats Central Management', + }), + order: 2, + mount, }); } } diff --git a/x-pack/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts index 56f428ff0b927..52d185e0d7dc9 100644 --- a/x-pack/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts @@ -39,15 +39,6 @@ export class TestingFrameworkAdapter implements FrameworkAdapter { return; } - public registerManagementSection(settings: { - id?: string; - name: string; - iconName: string; - order?: number; - }) { - throw new Error('not yet implamented'); - } - public registerManagementUI(settings: { sectionId?: string; name: string; order?: number }) { throw new Error('not yet implamented'); } diff --git a/x-pack/plugins/beats_management/public/lib/framework.ts b/x-pack/plugins/beats_management/public/lib/framework.ts index c850bdd8bc0ee..9e4271c683415 100644 --- a/x-pack/plugins/beats_management/public/lib/framework.ts +++ b/x-pack/plugins/beats_management/public/lib/framework.ts @@ -13,7 +13,6 @@ import { FrameworkAdapter } from './adapters/framework/adapter_types'; export class FrameworkLib { public waitUntilFrameworkReady = this.adapter.waitUntilFrameworkReady.bind(this.adapter); - public registerManagementSection = this.adapter.registerManagementSection.bind(this.adapter); public registerManagementUI = this.adapter.registerManagementUI.bind(this.adapter); constructor(private readonly adapter: FrameworkAdapter) {} diff --git a/x-pack/plugins/cross_cluster_replication/common/constants/index.ts b/x-pack/plugins/cross_cluster_replication/common/constants/index.ts index 797141b0996af..96884cf4bead8 100644 --- a/x-pack/plugins/cross_cluster_replication/common/constants/index.ts +++ b/x-pack/plugins/cross_cluster_replication/common/constants/index.ts @@ -24,8 +24,8 @@ export const APPS = { }; export const MANAGEMENT_ID = 'cross_cluster_replication'; -export const BASE_PATH = `/management/elasticsearch/${MANAGEMENT_ID}`; -export const BASE_PATH_REMOTE_CLUSTERS = '/management/elasticsearch/remote_clusters'; +export const BASE_PATH = `/management/data/${MANAGEMENT_ID}`; +export const BASE_PATH_REMOTE_CLUSTERS = '/management/data/remote_clusters'; export const API_BASE_PATH = '/api/cross_cluster_replication'; export const API_REMOTE_CLUSTERS_BASE_PATH = '/api/remote_clusters'; export const API_INDEX_MANAGEMENT_BASE_PATH = '/api/index_management'; diff --git a/x-pack/plugins/cross_cluster_replication/public/plugin.ts b/x-pack/plugins/cross_cluster_replication/public/plugin.ts index dfe9e4e657c30..561da838a4202 100644 --- a/x-pack/plugins/cross_cluster_replication/public/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/public/plugin.ts @@ -9,6 +9,7 @@ import { get } from 'lodash'; import { first } from 'rxjs/operators'; import { CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { PLUGIN, MANAGEMENT_ID } from '../common/constants'; import { init as initUiMetric } from './app/services/track_ui_metric'; import { init as initNotification } from './app/services/notifications'; @@ -22,7 +23,7 @@ export class CrossClusterReplicationPlugin implements Plugin { public setup(coreSetup: CoreSetup, plugins: PluginDependencies) { const { licensing, remoteClusters, usageCollection, management, indexManagement } = plugins; - const esSection = management.sections.getSection('elasticsearch'); + const esSection = management.sections.getSection(ManagementSectionId.Data); const { http, @@ -36,7 +37,7 @@ export class CrossClusterReplicationPlugin implements Plugin { initUiMetric(usageCollection); initNotification(toasts, fatalErrors); - const ccrApp = esSection!.registerApp({ + const ccrApp = esSection.registerApp({ id: MANAGEMENT_ID, title: PLUGIN.TITLE, order: 6, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap index dbdbe2b52bd56..d64c8c6239fcd 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap +++ b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap @@ -364,11 +364,11 @@ exports[`extend index management ilm summary extension should return extension w className="euiDescriptionList__description" > testy @@ -748,11 +748,11 @@ exports[`extend index management ilm summary extension should return extension w className="euiDescriptionList__description" > testy diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap b/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap index cbc735bb150f5..857a63826505e 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap @@ -94,7 +94,7 @@ exports[`policy table should show empty state when there are not any policies 1` { const [coreStart] = await getStartServices(); const { diff --git a/x-pack/plugins/index_management/common/constants/base_path.ts b/x-pack/plugins/index_management/common/constants/base_path.ts index 3a49e2870b609..c17d96250b1fb 100644 --- a/x-pack/plugins/index_management/common/constants/base_path.ts +++ b/x-pack/plugins/index_management/common/constants/base_path.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const BASE_PATH = '/management/elasticsearch/index_management/'; +export const BASE_PATH = '/management/data/index_management/'; diff --git a/x-pack/plugins/index_management/public/application/services/navigation.ts b/x-pack/plugins/index_management/public/application/services/navigation.ts index e56e8d474d780..8bd9fc2432232 100644 --- a/x-pack/plugins/index_management/public/application/services/navigation.ts +++ b/x-pack/plugins/index_management/public/application/services/navigation.ts @@ -19,8 +19,6 @@ export const getIndexListUri = (filter: any) => { export const getILMPolicyPath = (policyName: string) => { return encodeURI( - `#/management/elasticsearch/index_lifecycle_management/policies/edit/${encodeURIComponent( - policyName - )}` + `#/management/data/index_lifecycle_management/policies/edit/${encodeURIComponent(policyName)}` ); }; diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 78e80687abeb4..5fb8ce7207729 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -7,7 +7,7 @@ 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 { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { UIM_APP_NAME, PLUGIN } from '../common/constants'; import { httpService } from './application/services/http'; @@ -48,10 +48,10 @@ export class IndexMgmtUIPlugin { notificationService.setup(notifications); this.uiMetricService.setup(usageCollection); - management.sections.getSection('elasticsearch')!.registerApp({ + management.sections.getSection(ManagementSectionId.Data).registerApp({ id: PLUGIN.id, title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), - order: 2, + order: 0, mount: async params => { const { mountManagementSection } = await import('./application/mount_management_section'); const services = { diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx index 8bcf0e9ed5be5..bafb38459b17b 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx @@ -35,7 +35,7 @@ export const MetricsAlertDropdown = () => { icon="tableOfContents" key="manageLink" href={kibana.services?.application?.getUrlForApp( - 'kibana#/management/kibana/triggersActions/alerts' + 'kibana#/management/insightsAndAlerting/triggersActions/alerts' )} > diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx index d2904206875c7..a3cebcf33f386 100644 --- a/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx @@ -35,7 +35,7 @@ export const InventoryAlertDropdown = () => { icon="tableOfContents" key="manageLink" href={kibana.services?.application?.getUrlForApp( - 'kibana#/management/kibana/triggersActions/alerts' + 'kibana#/management/insightsAndAlerting/triggersActions/alerts' )} > diff --git a/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx b/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx index dd888639b6d07..d808b4f3b64aa 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx @@ -16,7 +16,7 @@ export const AlertDropdown = () => { const manageAlertsLinkProps = useLinkProps( { app: 'kibana', - hash: 'management/kibana/triggersActions/alerts', + hash: 'management/insightsAndAlerting/triggersActions/alerts', }, { hrefOnly: true, diff --git a/x-pack/plugins/ingest_pipelines/common/constants.ts b/x-pack/plugins/ingest_pipelines/common/constants.ts index edf681c276a84..de291e364e02f 100644 --- a/x-pack/plugins/ingest_pipelines/common/constants.ts +++ b/x-pack/plugins/ingest_pipelines/common/constants.ts @@ -11,7 +11,7 @@ export const PLUGIN_ID = 'ingest_pipelines'; export const PLUGIN_MIN_LICENSE_TYPE = basicLicense; -export const BASE_PATH = '/management/elasticsearch/ingest_pipelines'; +export const BASE_PATH = '/management/ingest/ingest_pipelines'; export const API_BASE_PATH = '/api/ingest_pipelines'; diff --git a/x-pack/plugins/ingest_pipelines/public/plugin.ts b/x-pack/plugins/ingest_pipelines/public/plugin.ts index 0ab46f386e83b..d537f8d68d7e4 100644 --- a/x-pack/plugins/ingest_pipelines/public/plugin.ts +++ b/x-pack/plugins/ingest_pipelines/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin } from 'src/core/public'; +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { PLUGIN_ID } from '../common/constants'; import { uiMetricService, apiService } from './application/services'; import { Dependencies } from './types'; @@ -20,7 +21,7 @@ export class IngestPipelinesPlugin implements Plugin { uiMetricService.setup(usageCollection); apiService.setup(http, uiMetricService); - management.sections.getSection('elasticsearch')!.registerApp({ + management.sections.getSection(ManagementSectionId.Ingest).registerApp({ id: PLUGIN_ID, order: 1, title: i18n.translate('xpack.ingestPipelines.appTitle', { diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap index 03421e66c77f5..e4411807dfa56 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AddLicense component when license is active should display correct verbiage 1`] = `""`; +exports[`AddLicense component when license is active should display correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; -exports[`AddLicense component when license is expired should display with correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; +exports[`AddLicense component when license is expired should display with correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap index 9ac8b14236685..b621e89efbee3 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap @@ -958,11 +958,11 @@ exports[`UploadLicense should display a modal when license requires acknowledgem className="euiFlexItem euiFlexItem--flexGrowZero" > { const [core] = await getStartServices(); const initialLicense = await plugins.licensing.license$.pipe(first()).toPromise(); diff --git a/x-pack/plugins/licensing/public/plugin.test.ts b/x-pack/plugins/licensing/public/plugin.test.ts index f68e1dcfaf62b..9f0019680d14b 100644 --- a/x-pack/plugins/licensing/public/plugin.test.ts +++ b/x-pack/plugins/licensing/public/plugin.test.ts @@ -367,7 +367,7 @@ describe('licensing plugin', () => { expect(coreStart.overlays.banners.add).toHaveBeenCalledTimes(1); expect(mountExpiredBannerMock).toHaveBeenCalledWith({ type: 'gold', - uploadUrl: '/app/kibana#/management/elasticsearch/license_management/upload_license', + uploadUrl: '/app/kibana#/management/stack/license_management/upload_license', }); }); }); diff --git a/x-pack/plugins/licensing/public/plugin.ts b/x-pack/plugins/licensing/public/plugin.ts index dab4c4048ce4c..31910d81b3434 100644 --- a/x-pack/plugins/licensing/public/plugin.ts +++ b/x-pack/plugins/licensing/public/plugin.ts @@ -148,7 +148,7 @@ export class LicensingPlugin implements Plugin { private showExpiredBanner(license: ILicense) { const uploadUrl = this.coreStart!.http.basePath.prepend( - '/app/kibana#/management/elasticsearch/license_management/upload_license' + '/app/kibana#/management/stack/license_management/upload_license' ); this.coreStart!.overlays.banners.add( mountExpiredBanner({ diff --git a/x-pack/plugins/logstash/public/application/breadcrumbs.js b/x-pack/plugins/logstash/public/application/breadcrumbs.js index 322b9860b3785..4ef259b84e24f 100644 --- a/x-pack/plugins/logstash/public/application/breadcrumbs.js +++ b/x-pack/plugins/logstash/public/application/breadcrumbs.js @@ -12,7 +12,7 @@ export function getPipelineListBreadcrumbs() { text: i18n.translate('xpack.logstash.pipelines.listBreadcrumb', { defaultMessage: 'Pipelines', }), - href: '#/management/logstash/pipelines', + href: '#/management/ingest/pipelines', }, ]; } diff --git a/x-pack/plugins/logstash/public/plugin.ts b/x-pack/plugins/logstash/public/plugin.ts index 7fbed5b3b8602..7d4afc0a4ea13 100644 --- a/x-pack/plugins/logstash/public/plugin.ts +++ b/x-pack/plugins/logstash/public/plugin.ts @@ -14,8 +14,8 @@ import { HomePublicPluginSetup, FeatureCatalogueCategory, } from '../../../../src/plugins/home/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { LicensingPluginSetup } from '../../licensing/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; // @ts-ignore import { LogstashLicenseService } from './services'; @@ -34,26 +34,23 @@ export class LogstashPlugin implements Plugin { const logstashLicense$ = plugins.licensing.license$.pipe( map(license => new LogstashLicenseService(license)) ); - const section = plugins.management.sections.register({ - id: 'logstash', - title: 'Logstash', - order: 30, - euiIconType: 'logoLogstash', - }); - const managementApp = section.registerApp({ - id: 'pipelines', - title: i18n.translate('xpack.logstash.managementSection.pipelinesTitle', { - defaultMessage: 'Pipelines', - }), - order: 10, - mount: async params => { - const [coreStart] = await core.getStartServices(); - const { renderApp } = await import('./application'); - const isMonitoringEnabled = 'monitoring' in plugins; - return renderApp(coreStart, params, isMonitoringEnabled, logstashLicense$); - }, - }); + const managementApp = plugins.management.sections + .getSection(ManagementSectionId.Ingest) + .registerApp({ + id: 'pipelines', + title: i18n.translate('xpack.logstash.managementSection.pipelinesTitle', { + defaultMessage: 'Logstash Pipelines', + }), + order: 1, + mount: async params => { + const [coreStart] = await core.getStartServices(); + const { renderApp } = await import('./application'); + const isMonitoringEnabled = 'monitoring' in plugins; + + return renderApp(coreStart, params, isMonitoringEnabled, logstashLicense$); + }, + }); this.licenseSubscription = logstashLicense$.subscribe((license: any) => { if (license.enableLinks) { @@ -74,7 +71,7 @@ export class LogstashPlugin implements Plugin { defaultMessage: 'Create, delete, update, and clone data ingestion pipelines.', }), icon: 'pipelineApp', - path: '/app/kibana#/management/logstash/pipelines', + path: '/app/kibana#/management/ingest/pipelines', showOnHomePage: true, category: FeatureCatalogueCategory.ADMIN, }); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx index 2d6505f5ce1f7..126fd25a536f6 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx @@ -184,7 +184,7 @@ export const DatavisualizerSelector: FC = () => { footer={ = ({ /> } description="" - href={`${basePath.get()}/app/kibana#/management/elasticsearch/index_management/indices/filter/${index}`} + href={`${basePath.get()}/app/kibana#/management/data/index_management/indices/filter/${index}`} /> diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js index 2a65ee06f2c2c..307fa79f5dea2 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js @@ -167,7 +167,7 @@ class CreateWatchService { saveWatch(watchModel) .then(() => { this.status.watch = this.STATUS.SAVED; - this.config.watcherEditURL = `${basePath.get()}/app/kibana#/management/elasticsearch/watcher/watches/watch/${id}/edit?_g=()`; + this.config.watcherEditURL = `${basePath.get()}/app/kibana#/management/insightsAndAlerting/watcher/watches/watch/${id}/edit?_g=()`; resolve({ id, url: this.config.watcherEditURL, diff --git a/x-pack/plugins/ml/public/application/management/index.ts b/x-pack/plugins/ml/public/application/management/index.ts index 6bc5c9b15074f..f15cdb12afb21 100644 --- a/x-pack/plugins/ml/public/application/management/index.ts +++ b/x-pack/plugins/ml/public/application/management/index.ts @@ -16,7 +16,11 @@ import { take } from 'rxjs/operators'; import { CoreSetup } from 'kibana/public'; import { MlStartDependencies, MlSetupDependencies } from '../../plugin'; -import { PLUGIN_ID, PLUGIN_ICON } from '../../../common/constants/app'; +import { + ManagementAppMountParams, + ManagementSectionId, +} from '../../../../../../src/plugins/management/public'; +import { PLUGIN_ID } from '../../../common/constants/app'; import { MINIMUM_FULL_LICENSE } from '../../../common/license'; export function initManagementSection( @@ -30,22 +34,13 @@ export function initManagementSection( management !== undefined && license.check(PLUGIN_ID, MINIMUM_FULL_LICENSE).state === 'valid' ) { - const mlSection = management.sections.register({ - id: PLUGIN_ID, - title: i18n.translate('xpack.ml.management.mlTitle', { - defaultMessage: 'Machine Learning', - }), - order: 100, - icon: PLUGIN_ICON, - }); - - mlSection.registerApp({ + management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({ id: 'jobsListLink', title: i18n.translate('xpack.ml.management.jobsListTitle', { - defaultMessage: 'Jobs list', + defaultMessage: 'Machine Learning Jobs', }), - order: 10, - async mount(params) { + order: 2, + async mount(params: ManagementAppMountParams) { const { mountApp } = await import('./jobs_list'); return mountApp(core, params); }, diff --git a/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx b/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx index 3e4e9cfbd2b66..87a7156b6f52e 100644 --- a/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx @@ -42,7 +42,7 @@ export const OverviewSideBar: FC = ({ createAnomalyDetectionJobDisabled } const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; const docsLink = `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/xpack-ml.html`; - const transformsLink = `${basePath.get()}/app/kibana#/management/elasticsearch/transform`; + const transformsLink = `${basePath.get()}/app/kibana#/management/data/transform`; return ( diff --git a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js index feda891c1ce29..69d7727f9a20a 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js @@ -288,7 +288,7 @@ const handleClickIncompatibleLicense = (scope, clusterName) => { }; const handleClickInvalidLicense = (scope, clusterName) => { - const licensingPath = `${Legacy.shims.getBasePath()}/app/kibana#/management/elasticsearch/license_management/home`; + const licensingPath = `${Legacy.shims.getBasePath()}/app/kibana#/management/stack/license_management/home`; licenseWarning(scope, { title: toMountPoint( diff --git a/x-pack/plugins/monitoring/public/components/license/index.js b/x-pack/plugins/monitoring/public/components/license/index.js index 085cc9082cf53..e8ea1f8df227a 100644 --- a/x-pack/plugins/monitoring/public/components/license/index.js +++ b/x-pack/plugins/monitoring/public/components/license/index.js @@ -169,7 +169,7 @@ const LicenseUpdateInfoForRemote = ({ isPrimaryCluster }) => { export function License(props) { const { status, type, isExpired, expiryDate } = props; - const licenseManagement = `${Legacy.shims.getBasePath()}/app/kibana#/management/elasticsearch/license_management`; + const licenseManagement = `${Legacy.shims.getBasePath()}/app/kibana#/management/stack/license_management`; return ( diff --git a/x-pack/plugins/remote_clusters/public/application/constants/paths.ts b/x-pack/plugins/remote_clusters/public/application/constants/paths.ts index 23fe6758542c9..770447ce33f93 100644 --- a/x-pack/plugins/remote_clusters/public/application/constants/paths.ts +++ b/x-pack/plugins/remote_clusters/public/application/constants/paths.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CRUD_APP_BASE_PATH: string = '/management/elasticsearch/remote_clusters'; +export const CRUD_APP_BASE_PATH: string = '/management/data/remote_clusters'; diff --git a/x-pack/plugins/remote_clusters/public/plugin.ts b/x-pack/plugins/remote_clusters/public/plugin.ts index 22f98e94748d8..fde8ffa511319 100644 --- a/x-pack/plugins/remote_clusters/public/plugin.ts +++ b/x-pack/plugins/remote_clusters/public/plugin.ts @@ -6,6 +6,8 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, CoreStart, PluginInitializerContext } from 'kibana/public'; + +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { init as initBreadcrumbs } from './application/services/breadcrumb'; import { init as initDocumentation } from './application/services/documentation'; import { init as initHttp } from './application/services/http'; @@ -31,13 +33,14 @@ export class RemoteClustersUIPlugin } = this.initializerContext.config.get(); if (isRemoteClustersUiEnabled) { - const esSection = management.sections.getSection('elasticsearch'); + const esSection = management.sections.getSection(ManagementSectionId.Data); - esSection!.registerApp({ + esSection.registerApp({ id: 'remote_clusters', title: i18n.translate('xpack.remoteClusters.appTitle', { defaultMessage: 'Remote Clusters', }), + order: 7, mount: async ({ element, setBreadcrumbs }) => { const [core] = await getStartServices(); const { diff --git a/x-pack/plugins/reporting/public/plugin.tsx b/x-pack/plugins/reporting/public/plugin.tsx index 66366cc0b520d..f600b1ebbb96c 100644 --- a/x-pack/plugins/reporting/public/plugin.tsx +++ b/x-pack/plugins/reporting/public/plugin.tsx @@ -17,9 +17,9 @@ import { Plugin, PluginInitializerContext, } from 'src/core/public'; -import { ManagementSetup } from 'src/plugins/management/public'; import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { JobId, JobStatusBuckets } from '../'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { CONTEXT_MENU_TRIGGER } from '../../../../src/plugins/embeddable/public'; import { FeatureCatalogueCategory, @@ -117,10 +117,10 @@ export class ReportingPublicPlugin implements Plugin { category: FeatureCatalogueCategory.ADMIN, }); - management.sections.getSection('kibana')!.registerApp({ + management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({ id: 'reporting', title: this.title, - order: 15, + order: 1, mount: async params => { const [start] = await getStartServices(); params.setBreadcrumbs([{ text: this.breadcrumbText }]); diff --git a/x-pack/plugins/rollup/public/crud_app/constants/paths.js b/x-pack/plugins/rollup/public/crud_app/constants/paths.js index 83a7ca6bc5967..44829f38e79cd 100644 --- a/x-pack/plugins/rollup/public/crud_app/constants/paths.js +++ b/x-pack/plugins/rollup/public/crud_app/constants/paths.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CRUD_APP_BASE_PATH = '/management/elasticsearch/rollup_jobs'; +export const CRUD_APP_BASE_PATH = '/management/data/rollup_jobs'; diff --git a/x-pack/plugins/rollup/public/plugin.ts b/x-pack/plugins/rollup/public/plugin.ts index 0e0333cf30f17..b2e793d7e75e9 100644 --- a/x-pack/plugins/rollup/public/plugin.ts +++ b/x-pack/plugins/rollup/public/plugin.ts @@ -18,7 +18,7 @@ import { } from '../../../../src/plugins/home/public'; // @ts-ignore import { CRUD_APP_BASE_PATH } from './crud_app/constants'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { IndexManagementPluginSetup } from '../../index_management/public'; import { IndexPatternManagementSetup } from '../../../../src/plugins/index_pattern_management/public'; // @ts-ignore @@ -77,26 +77,23 @@ export class RollupPlugin implements Plugin { }); } - const esSection = management.sections.getSection('elasticsearch'); - if (esSection) { - esSection.registerApp({ - id: 'rollup_jobs', - title: i18n.translate('xpack.rollupJobs.appTitle', { defaultMessage: 'Rollup Jobs' }), - order: 5, - async mount(params) { - params.setBreadcrumbs([ - { - text: i18n.translate('xpack.rollupJobs.breadcrumbsTitle', { - defaultMessage: 'Rollup Jobs', - }), - }, - ]); - const { renderApp } = await import('./application'); + management.sections.getSection(ManagementSectionId.Data).registerApp({ + id: 'rollup_jobs', + title: i18n.translate('xpack.rollupJobs.appTitle', { defaultMessage: 'Rollup Jobs' }), + order: 4, + async mount(params) { + params.setBreadcrumbs([ + { + text: i18n.translate('xpack.rollupJobs.breadcrumbsTitle', { + defaultMessage: 'Rollup Jobs', + }), + }, + ]); + const { renderApp } = await import('./application'); - return renderApp(core, params); - }, - }); - } + return renderApp(core, params); + }, + }); } start(core: CoreStart) { diff --git a/x-pack/plugins/security/public/management/management_service.test.ts b/x-pack/plugins/security/public/management/management_service.test.ts index 53c12ad7ab12c..466e1aa9b3c68 100644 --- a/x-pack/plugins/security/public/management/management_service.test.ts +++ b/x-pack/plugins/security/public/management/management_service.test.ts @@ -27,9 +27,8 @@ describe('ManagementService', () => { const mockSection = { registerApp: jest.fn() }; const managementSetup = { sections: { - getSection: jest.fn(), + getSection: jest.fn().mockReturnValue(mockSection), getAllSections: jest.fn(), - register: jest.fn().mockReturnValue(mockSection), }, }; @@ -42,14 +41,6 @@ describe('ManagementService', () => { management: managementSetup, }); - expect(managementSetup.sections.register).toHaveBeenCalledTimes(1); - expect(managementSetup.sections.register).toHaveBeenCalledWith({ - id: 'security', - title: 'Security', - order: 100, - euiIconType: 'securityApp', - }); - expect(mockSection.registerApp).toHaveBeenCalledTimes(4); expect(mockSection.registerApp).toHaveBeenCalledWith({ id: 'users', @@ -96,9 +87,8 @@ describe('ManagementService', () => { authc: securityMock.createSetup().authc, management: { sections: { - getSection: jest.fn(), + getSection: jest.fn().mockReturnValue({ registerApp: jest.fn() }), getAllSections: jest.fn(), - register: jest.fn().mockReturnValue({ registerApp: jest.fn() }), }, }, }); diff --git a/x-pack/plugins/security/public/management/management_service.ts b/x-pack/plugins/security/public/management/management_service.ts index 7c4c470730ffe..2dc3ecbd9f3a2 100644 --- a/x-pack/plugins/security/public/management/management_service.ts +++ b/x-pack/plugins/security/public/management/management_service.ts @@ -5,12 +5,12 @@ */ import { Subscription } from 'rxjs'; -import { i18n } from '@kbn/i18n'; import { StartServicesAccessor, FatalErrorsSetup } from 'src/core/public'; import { ManagementApp, ManagementSetup, ManagementStart, + ManagementSectionId, } from '../../../../../src/plugins/management/public'; import { SecurityLicense } from '../../common/licensing'; import { AuthenticationServiceSetup } from '../authentication'; @@ -39,14 +39,7 @@ export class ManagementService { setup({ getStartServices, management, authc, license, fatalErrors }: SetupParams) { this.license = license; - const securitySection = management.sections.register({ - id: 'security', - title: i18n.translate('xpack.security.management.securityTitle', { - defaultMessage: 'Security', - }), - order: 100, - euiIconType: 'securityApp', - }); + const securitySection = management.sections.getSection(ManagementSectionId.Security); securitySection.registerApp(usersManagementApp.create({ authc, getStartServices })); securitySection.registerApp( @@ -58,7 +51,7 @@ export class ManagementService { start({ management }: StartParams) { this.licenseFeaturesSubscription = this.license.features$.subscribe(async features => { - const securitySection = management.sections.getSection('security')!; + const securitySection = management.sections.getSection(ManagementSectionId.Security); const securityManagementAppsStatuses: Array<[ManagementApp, boolean]> = [ [securitySection.getApp(usersManagementApp.id)!, features.showLinks], diff --git a/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx b/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx index 58112732bea3b..4a21eb0fbcf23 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx @@ -77,7 +77,7 @@ export const SelectRuleType: React.FC = ({ const setQuery = useCallback(() => setType('query'), [setType]); const mlCardDisabled = isReadOnly || !hasValidLicense || !isMlAdmin; const licensingUrl = useKibana().services.application.getUrlForApp('kibana', { - path: '#/management/elasticsearch/license_management', + path: '#/management/stack/license_management', }); return ( diff --git a/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap b/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap index 113612200d367..87cf9fb18adf3 100644 --- a/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap +++ b/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap @@ -50,7 +50,7 @@ exports[`JobsTableFilters renders correctly against snapshot 1`] = ` grow={false} > diff --git a/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx b/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx index 5a483e0d5b876..eda6b0f28499c 100644 --- a/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx +++ b/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx @@ -59,7 +59,7 @@ export const UpgradeContentsComponent = () => ( diff --git a/x-pack/plugins/snapshot_restore/public/application/constants/index.ts b/x-pack/plugins/snapshot_restore/public/application/constants/index.ts index ea6f5c80b9343..ad95c19870d15 100644 --- a/x-pack/plugins/snapshot_restore/public/application/constants/index.ts +++ b/x-pack/plugins/snapshot_restore/public/application/constants/index.ts @@ -6,7 +6,7 @@ import { DAY } from '../../shared_imports'; -export const BASE_PATH = '/management/elasticsearch/snapshot_restore'; +export const BASE_PATH = '/management/data/snapshot_restore'; export const DEFAULT_SECTION: Section = 'snapshots'; export type Section = 'repositories' | 'snapshots' | 'restore_status' | 'policies'; diff --git a/x-pack/plugins/snapshot_restore/public/plugin.ts b/x-pack/plugins/snapshot_restore/public/plugin.ts index d966d0c32651c..13a15c5711ba9 100644 --- a/x-pack/plugins/snapshot_restore/public/plugin.ts +++ b/x-pack/plugins/snapshot_restore/public/plugin.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, PluginInitializerContext } from 'src/core/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { PLUGIN } from '../common/constants'; import { ClientConfigType } from './types'; @@ -40,12 +40,12 @@ export class SnapshotRestoreUIPlugin { textService.setup(i18n); httpService.setup(http); - management.sections.getSection('elasticsearch')!.registerApp({ + management.sections.getSection(ManagementSectionId.Data).registerApp({ id: PLUGIN.id, title: i18n.translate('xpack.snapshotRestore.appTitle', { defaultMessage: 'Snapshot and Restore', }), - order: 7, + order: 3, mount: async params => { const { mountManagementSection } = await import('./application/mount_management_section'); const services = { diff --git a/x-pack/plugins/spaces/public/management/management_service.test.ts b/x-pack/plugins/spaces/public/management/management_service.test.ts index 782c261be9664..7f8acccf71d88 100644 --- a/x-pack/plugins/spaces/public/management/management_service.test.ts +++ b/x-pack/plugins/spaces/public/management/management_service.test.ts @@ -39,7 +39,7 @@ describe('ManagementService', () => { expect(mockKibanaSection.registerApp).toHaveBeenCalledWith({ id: 'spaces', title: 'Spaces', - order: 10, + order: 2, mount: expect.any(Function), }); }); diff --git a/x-pack/plugins/spaces/public/management/management_service.tsx b/x-pack/plugins/spaces/public/management/management_service.tsx index cec4bee1373ca..297df07636849 100644 --- a/x-pack/plugins/spaces/public/management/management_service.tsx +++ b/x-pack/plugins/spaces/public/management/management_service.tsx @@ -4,8 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ManagementSetup, ManagementApp } from 'src/plugins/management/public'; import { StartServicesAccessor, Capabilities } from 'src/core/public'; +import { + ManagementSetup, + ManagementApp, + ManagementSectionId, +} from '../../../../../src/plugins/management/public'; import { SecurityLicense } from '../../../security/public'; import { SpacesManager } from '../spaces_manager'; import { PluginsStart } from '../plugin'; @@ -25,12 +29,11 @@ export class ManagementService { private registeredSpacesManagementApp?: ManagementApp; public setup({ getStartServices, management, spacesManager, securityLicense }: SetupDeps) { - const kibanaSection = management.sections.getSection('kibana'); - if (kibanaSection) { - this.registeredSpacesManagementApp = kibanaSection.registerApp( + this.registeredSpacesManagementApp = management.sections + .getSection(ManagementSectionId.Kibana) + .registerApp( spacesManagementApp.create({ getStartServices, spacesManager, securityLicense }) ); - } } public start({ capabilities }: StartDeps) { diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx index 61c872ec9269c..92c78d63d1b2e 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -70,7 +70,7 @@ describe('spacesManagementApp', () => { Object { "id": "spaces", "mount": [Function], - "order": 10, + "order": 2, "title": "Spaces", } `); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index 92b369807b0da..079cf2234b13b 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -28,7 +28,7 @@ export const spacesManagementApp = Object.freeze({ create({ getStartServices, spacesManager, securityLicense }: CreateParams) { return { id: this.id, - order: 10, + order: 2, title: i18n.translate('xpack.spaces.displayName', { defaultMessage: 'Spaces', }), diff --git a/x-pack/plugins/spaces/public/plugin.test.ts b/x-pack/plugins/spaces/public/plugin.test.ts index d879a318fe815..a98f593f546a0 100644 --- a/x-pack/plugins/spaces/public/plugin.test.ts +++ b/x-pack/plugins/spaces/public/plugin.test.ts @@ -7,7 +7,7 @@ import { coreMock } from 'src/core/public/mocks'; import { SpacesPlugin } from './plugin'; import { homePluginMock } from '../../../../src/plugins/home/public/mocks'; -import { ManagementSection } from '../../../../src/plugins/management/public'; +import { ManagementSection, ManagementSectionId } from '../../../../src/plugins/management/public'; import { managementPluginMock } from '../../../../src/plugins/management/public/mocks'; import { advancedSettingsMock } from '../../../../src/plugins/advanced_settings/public/mocks'; import { featuresPluginMock } from '../../features/public/mocks'; @@ -35,7 +35,7 @@ describe('Spaces plugin', () => { const kibanaSection = new ManagementSection( { - id: 'kibana', + id: ManagementSectionId.Kibana, title: 'Mock Kibana Section', order: 1, }, diff --git a/x-pack/plugins/transform/public/app/constants/index.ts b/x-pack/plugins/transform/public/app/constants/index.ts index 5d71980c83714..3156fae7545b1 100644 --- a/x-pack/plugins/transform/public/app/constants/index.ts +++ b/x-pack/plugins/transform/public/app/constants/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CLIENT_BASE_PATH = '/management/elasticsearch/transform/'; +export const CLIENT_BASE_PATH = '/management/data/transform/'; export enum SECTION_SLUG { HOME = 'transform_management', diff --git a/x-pack/plugins/transform/public/plugin.ts b/x-pack/plugins/transform/public/plugin.ts index 563a569fe95b5..1411f80cecd2e 100644 --- a/x-pack/plugins/transform/public/plugin.ts +++ b/x-pack/plugins/transform/public/plugin.ts @@ -7,8 +7,8 @@ import { i18n as kbnI18n } from '@kbn/i18n'; import { CoreSetup } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { ManagementSetup } from 'src/plugins/management/public'; import { HomePublicPluginSetup } from 'src/plugins/home/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { registerFeature } from './register_feature'; export interface PluginsDependencies { @@ -22,20 +22,18 @@ export class TransformUiPlugin { const { management, home } = pluginsSetup; // Register management section - const esSection = management.sections.getSection('elasticsearch'); - if (esSection !== undefined) { - esSection.registerApp({ - id: 'transform', - title: kbnI18n.translate('xpack.transform.appTitle', { - defaultMessage: 'Transforms', - }), - order: 4, - mount: async params => { - const { mountManagementSection } = await import('./app/mount_management_section'); - return mountManagementSection(coreSetup, params); - }, - }); - } + const esSection = management.sections.getSection(ManagementSectionId.Data); + esSection.registerApp({ + id: 'transform', + title: kbnI18n.translate('xpack.transform.appTitle', { + defaultMessage: 'Transforms', + }), + order: 5, + mount: async params => { + const { mountManagementSection } = await import('./app/mount_management_section'); + return mountManagementSection(coreSetup, params); + }, + }); registerFeature(home); } diff --git a/x-pack/plugins/transform/public/register_feature.ts b/x-pack/plugins/transform/public/register_feature.ts index 708dfcb70c67a..c81a18a3def87 100644 --- a/x-pack/plugins/transform/public/register_feature.ts +++ b/x-pack/plugins/transform/public/register_feature.ts @@ -22,7 +22,7 @@ export const registerFeature = (home: HomePublicPluginSetup) => { 'Use transforms to pivot existing Elasticsearch indices into summarized or entity-centric indices.', }), icon: 'managementApp', // there is currently no Transforms icon, so using the general management app icon - path: '/app/kibana#/management/elasticsearch/transform', + path: '/app/kibana#/management/data/transform', showOnHomePage: true, category: FeatureCatalogueCategory.ADMIN, }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index bc4a1ace2967b..219e922a0158c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2419,7 +2419,6 @@ "kibana-react.tableListView.listing.table.editActionDescription": "編集", "kibana-react.tableListView.listing.table.editActionName": "編集", "kibana-react.tableListView.listing.unableToDeleteDangerMessage": "{entityName} を削除できません", - "management.connectDataDisplayName": "データに接続", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "すべてのデータに完全集約を実行", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "標準インデックスパターン", "indexPatternManagement.editIndexPattern.createIndex.defaultTypeName": "インデックスパターン", @@ -4759,8 +4758,6 @@ "xpack.beatsManagement.breadcrumb.beatTags": "{beatId} のビートタグ", "xpack.beatsManagement.breadcrumb.configurationTags": "構成タグ", "xpack.beatsManagement.breadcrumb.enrolledBeats": "登録済みのビート", - "xpack.beatsManagement.centralManagementLinkLabel": "集中管理", - "xpack.beatsManagement.centralManagementSectionLabel": "ビート", "xpack.beatsManagement.config.other.error": "有効な YAML フォーマットを使用してください", "xpack.beatsManagement.config.otherConfigDescription": "YAML フォーマットで Filebeat インプットの他の設定を指定します", "xpack.beatsManagement.config.otherConfigLabel": "他の構成", @@ -9975,7 +9972,6 @@ "xpack.ml.management.jobsList.noGrantedPrivilegesDescription": "ML ジョブを管理するパーミッションがありません", "xpack.ml.management.jobsList.noPermissionToAccessLabel": "ML ジョブへのアクセスにはパーミッションが必要です", "xpack.ml.management.jobsListTitle": "ジョブリスト", - "xpack.ml.management.mlTitle": "機械学習", "xpack.ml.messagebarService.errorTitle": "エラーが発生しました", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 他のすべてのリクエストはキャンセルされました。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "フィールド値の例のサンプルをトークン化することができませんでした。{message}", @@ -12827,7 +12823,6 @@ "xpack.security.management.roles.statusColumnName": "ステータス", "xpack.security.management.roles.subtitle": "ユーザーのグループにロールを適用してスタック全体のパーミッションを管理", "xpack.security.management.rolesTitle": "ロール", - "xpack.security.management.securityTitle": "セキュリティ", "xpack.security.management.users.confirmDelete.cancelButtonLabel": "キャンセル", "xpack.security.management.users.confirmDelete.confirmButtonLabel": "削除", "xpack.security.management.users.confirmDelete.deleteMultipleUsersTitle": "{userLength} ユーザーの削除", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b22b607f63aeb..954162647bf83 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2420,7 +2420,6 @@ "kibana-react.tableListView.listing.table.editActionDescription": "编辑", "kibana-react.tableListView.listing.table.editActionName": "编辑", "kibana-react.tableListView.listing.unableToDeleteDangerMessage": "无法删除{entityName}", - "management.connectDataDisplayName": "连接数据", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "对任何数据执行完全聚合", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "标准索引模式", "indexPatternManagement.editIndexPattern.createIndex.defaultTypeName": "索引模式", @@ -4760,8 +4759,6 @@ "xpack.beatsManagement.breadcrumb.beatTags": "{beatId} 的 Beat 标记", "xpack.beatsManagement.breadcrumb.configurationTags": "配置标记", "xpack.beatsManagement.breadcrumb.enrolledBeats": "已注册 Beats", - "xpack.beatsManagement.centralManagementLinkLabel": "集中管理", - "xpack.beatsManagement.centralManagementSectionLabel": "Beats", "xpack.beatsManagement.config.other.error": "使用有效的 YAML 格式", "xpack.beatsManagement.config.otherConfigDescription": "使用 YAML 格式指定 Filebeat 输入的其他设置", "xpack.beatsManagement.config.otherConfigLabel": "其他配置", @@ -9981,7 +9978,6 @@ "xpack.ml.management.jobsList.noGrantedPrivilegesDescription": "您无权管理 ML 作业", "xpack.ml.management.jobsList.noPermissionToAccessLabel": "您需要访问 ML 作业的权限", "xpack.ml.management.jobsListTitle": "作业列表", - "xpack.ml.management.mlTitle": "Machine Learning", "xpack.ml.messagebarService.errorTitle": "发生了错误", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 所有其他请求已取消。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "无法对示例字段值样本进行分词。{message}", @@ -12834,7 +12830,6 @@ "xpack.security.management.roles.statusColumnName": "状态", "xpack.security.management.roles.subtitle": "将角色应用到用户组并管理整个堆栈的权限。", "xpack.security.management.rolesTitle": "角色", - "xpack.security.management.securityTitle": "安全性", "xpack.security.management.users.confirmDelete.cancelButtonLabel": "取消", "xpack.security.management.users.confirmDelete.confirmButtonLabel": "删除", "xpack.security.management.users.confirmDelete.deleteMultipleUsersTitle": "删除 {userLength} 用户", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 2f5172e8b386a..265cfddab4c06 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -7,7 +7,7 @@ export { BASE_ALERT_API_PATH } from '../../../../alerting/common'; export { BASE_ACTION_API_PATH } from '../../../../actions/common'; -export const BASE_PATH = '/management/kibana/triggersActions'; +export const BASE_PATH = '/management/insightsAndAlerting/triggersActions'; export type Section = 'connectors' | 'alerts'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx index 4f5007949f8b1..1da9abea40dba 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx @@ -127,7 +127,7 @@ describe('connector_add_flyout', () => { const manageLink = callout.find('EuiButton'); expect(manageLink).toHaveLength(1); expect(manageLink.getElements()[0].props.href).toMatchInlineSnapshot( - `"/app/kibana#/management/elasticsearch/license_management/"` + `"/app/kibana#/management/stack/license_management/"` ); const subscriptionLink = callout.find('EuiButtonEmpty'); diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 99a3d65589e8e..016b564c47d00 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -12,7 +12,7 @@ import { registerBuiltInAlertTypes } from './application/components/builtin_aler import { hasShowActionsCapability, hasShowAlertsCapability } from './application/lib/capabilities'; import { ActionTypeModel, AlertTypeModel } from './types'; import { TypeRegistry } from './application/type_registry'; -import { ManagementStart } from '../../../../src/plugins/management/public'; +import { ManagementStart, ManagementSectionId } from '../../../../src/plugins/management/public'; import { boot } from './application/boot'; import { ChartsPluginStart } from '../../../../src/plugins/charts/public'; import { PluginStartContract as AlertingStart } from '../../alerting/public'; @@ -73,12 +73,12 @@ export class Plugin // Don't register routes when user doesn't have access to the application if (canShowActions || canShowAlerts) { - plugins.management.sections.getSection('kibana')!.registerApp({ + plugins.management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({ id: 'triggersActions', title: i18n.translate('xpack.triggersActionsUI.managementSection.displayName', { defaultMessage: 'Alerts and Actions', }), - order: 7, + order: 0, mount: params => { boot({ dataPlugin: plugins.data, diff --git a/x-pack/plugins/upgrade_assistant/public/plugin.ts b/x-pack/plugins/upgrade_assistant/public/plugin.ts index bb0f21062c151..be00a030d5a27 100644 --- a/x-pack/plugins/upgrade_assistant/public/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/public/plugin.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public'; import { CloudSetup } from '../../cloud/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { NEXT_MAJOR_VERSION } from '../common/version'; import { Config } from '../common/config'; @@ -24,7 +24,7 @@ export class UpgradeAssistantUIPlugin implements Plugin { if (!enabled) { return; } - const appRegistrar = management.sections.getSection('elasticsearch')!; + const appRegistrar = management.sections.getSection(ManagementSectionId.Stack); const isCloudEnabled = Boolean(cloud?.isCloudEnabled); appRegistrar.registerApp({ @@ -33,7 +33,7 @@ export class UpgradeAssistantUIPlugin implements Plugin { defaultMessage: '{version} Upgrade Assistant', values: { version: `${NEXT_MAJOR_VERSION}.0` }, }), - order: 1000, + order: 1, async mount(params) { const { mountManagementSection } = await import('./application/mount_management_section'); return mountManagementSection(coreSetup, isCloudEnabled, params); diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap index fb40a42e47f75..5ae90a1613575 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap @@ -28,7 +28,7 @@ Array [

@@ -64,7 +64,7 @@ exports[`ShowLicenseInfo shallow renders without errors 1`] = `

Start free 14-day trial diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap index 3ee949b9712bd..df9de8c2ad03d 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap @@ -160,7 +160,7 @@ exports[`ML Flyout component shows license info if no ml available 1`] = `

diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx index fae81177a728c..33fbbb117a11a 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx @@ -22,7 +22,7 @@ export const ShowLicenseInfo = () => {

{labels.START_TRAIL_DESC}

{labels.START_TRAIL} diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx index cba96cd2fe5b1..0cdb3c0feb71f 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -61,7 +61,7 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ ', () => { expect(findTestSubject(idColumn, `watchIdColumn-${watch1.id}`).length).toBe(1); expect(findTestSubject(idColumn, `watchIdColumn-${watch1.id}`).props().href).toEqual( - `#/management/elasticsearch/watcher/watches/watch/${watch1.id}/status` + `#/management/insightsAndAlerting/watcher/watches/watch/${watch1.id}/status` ); }); diff --git a/x-pack/plugins/watcher/public/application/app.tsx b/x-pack/plugins/watcher/public/application/app.tsx index f4b9441719386..8a6d2746237e9 100644 --- a/x-pack/plugins/watcher/public/application/app.tsx +++ b/x-pack/plugins/watcher/public/application/app.tsx @@ -69,7 +69,7 @@ export const App = (deps: AppDeps) => { iconType="help" > {message}{' '} - + { values={{ watchName: watch.name, watchStatusLink: ( - + { return ( {id} @@ -326,7 +326,7 @@ export const WatchList = () => { )} iconType="pencil" color="primary" - href={`#/management/elasticsearch/watcher/watches/watch/${watch.id}/edit`} + href={`#/management/insightsAndAlerting/watcher/watches/watch/${watch.id}/edit`} data-test-subj="editWatchButton" /> diff --git a/x-pack/plugins/watcher/public/plugin.ts b/x-pack/plugins/watcher/public/plugin.ts index 6de21bc27d48c..6496c742fcb40 100644 --- a/x-pack/plugins/watcher/public/plugin.ts +++ b/x-pack/plugins/watcher/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, CoreStart } from 'kibana/public'; import { first, map, skip } from 'rxjs/operators'; +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { LicenseStatus } from '../common/types/license_status'; @@ -28,14 +29,15 @@ export class WatcherUIPlugin implements Plugin { { notifications, http, uiSettings, getStartServices }: CoreSetup, { licensing, management, data, home, charts }: Dependencies ) { - const esSection = management.sections.getSection('elasticsearch'); + const esSection = management.sections.getSection(ManagementSectionId.InsightsAndAlerting); - const watcherESApp = esSection!.registerApp({ + const watcherESApp = esSection.registerApp({ id: 'watcher', title: i18n.translate( 'xpack.watcher.sections.watchList.managementSection.watcherDisplayName', { defaultMessage: 'Watcher' } ), + order: 3, mount: async ({ element, setBreadcrumbs }) => { const [core] = await getStartServices(); const { i18n: i18nDep, docLinks, savedObjects } = core; @@ -74,7 +76,7 @@ export class WatcherUIPlugin implements Plugin { defaultMessage: 'Detect changes in your data by creating, managing, and monitoring alerts.', }), icon: 'watchesApp', - path: '/app/kibana#/management/elasticsearch/watcher/watches', + path: '/app/kibana#/management/insightsAndAlerting/watcher/watches', showOnHomePage: false, }; diff --git a/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts b/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts index d9d387f89640a..8c96fdbd0cbef 100644 --- a/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts +++ b/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts @@ -21,7 +21,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await security.testUser.setRoles(['kibana_admin', 'reporting_user']); await esArchiver.load('empty_kibana'); await esArchiver.load('reporting/archived_reports'); - await pageObjects.common.navigateToActualUrl('kibana', '/management/kibana/reporting'); + await pageObjects.common.navigateToApp('reporting'); await testSubjects.existOrFail('reportJobListing', { timeout: 200000 }); }); diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 0639782dc7f7b..94976511a1b12 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -114,7 +114,7 @@ export default async function({ readConfigFile }) { }, logstashPipelines: { pathname: '/app/kibana', - hash: '/management/logstash/pipelines', + hash: '/management/ingest/pipelines', }, maps: { pathname: '/app/maps', @@ -155,7 +155,7 @@ export default async function({ readConfigFile }) { }, rollupJob: { pathname: '/app/kibana', - hash: '/management/elasticsearch/rollup_jobs/', + hash: '/management/data/rollup_jobs/', }, apiKeys: { pathname: '/app/kibana', @@ -163,46 +163,46 @@ export default async function({ readConfigFile }) { }, licenseManagement: { pathname: '/app/kibana', - hash: '/management/elasticsearch/license_management', + hash: '/management/stack/license_management', }, indexManagement: { pathname: '/app/kibana', - hash: '/management/elasticsearch/index_management', + hash: '/management/data/index_management', }, indexLifecycleManagement: { pathname: '/app/kibana', - hash: '/management/elasticsearch/index_lifecycle_management', + hash: '/management/data/index_lifecycle_management', }, ingestPipelines: { pathname: '/app/kibana', - hash: '/management/elasticsearch/ingest_pipelines', + hash: '/management/ingest/ingest_pipelines', }, snapshotRestore: { pathname: '/app/kibana', - hash: '/management/elasticsearch/snapshot_restore', + hash: '/management/data/snapshot_restore', }, crossClusterReplication: { pathname: '/app/kibana', - hash: '/management/elasticsearch/cross_cluster_replication', + hash: '/management/data/cross_cluster_replication', }, remoteClusters: { pathname: '/app/kibana', - hash: '/management/elasticsearch/remote_clusters', + hash: '/management/data/remote_clusters', }, apm: { pathname: '/app/apm', }, watcher: { pathname: '/app/kibana', - hash: '/management/elasticsearch/watcher/watches/', + hash: '/management/insightsAndAlerting/watcher/watches/', }, transform: { pathname: '/app/kibana/', - hash: '/management/elasticsearch/transform', + hash: '/management/data/transform', }, reporting: { pathname: '/app/kibana/', - hash: '/management/kibana/reporting', + hash: '/management/insightsAndAlerting/reporting', }, }, diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index 50de76d67e06b..f69c17c96f074 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -51,7 +51,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { ...xpackFunctionalConfig.get('apps'), triggersActions: { pathname: '/app/kibana', - hash: '/management/kibana/triggersActions', + hash: '/management/insightsAndAlerting/triggersActions', }, }, esTestCluster: { From 74611e742d6a805cff8dedb8f93e65b2f84cb10b Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Sat, 16 May 2020 09:04:51 +0200 Subject: [PATCH 11/14] Use ES API from start contract (#66157) * watcher uses es api from start * CCR uses ES API from start contract * Rollup uses ES API from start contract * Transform uses ES API from start contract * Snapshot_restore uses ES API from start contract * remove excessive logging. platform logs all the lifecycles * file uploader uses ES API from start contract * remove unnecessary async * use async getter * update rollup custom client usage * address cj comment * roll back changes. maps tests are failing Co-authored-by: Elastic Machine --- .../server/plugin.ts | 26 +++++++++++---- x-pack/plugins/rollup/server/plugin.ts | 32 ++++++++++++++----- .../plugins/snapshot_restore/server/plugin.ts | 26 +++++++++------ x-pack/plugins/transform/server/plugin.ts | 27 ++++++++++++---- x-pack/plugins/watcher/server/plugin.ts | 30 +++++++++++------ 5 files changed, 100 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/cross_cluster_replication/server/plugin.ts b/x-pack/plugins/cross_cluster_replication/server/plugin.ts index 25c99803480f3..7ef085a21ac1a 100644 --- a/x-pack/plugins/cross_cluster_replication/server/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/server/plugin.ts @@ -15,6 +15,7 @@ import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import { CoreSetup, + ICustomClusterClient, Plugin, Logger, PluginInitializerContext, @@ -36,6 +37,13 @@ interface CrossClusterReplicationContext { client: IScopedClusterClient; } +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + // Extend the elasticsearchJs client with additional endpoints. + const esClientConfig = { plugins: [elasticsearchJsPlugin] }; + return core.elasticsearch.legacy.createClient('crossClusterReplication', esClientConfig); +} + const ccrDataEnricher = async (indicesList: Index[], callWithRequest: APICaller) => { if (!indicesList?.length) { return indicesList; @@ -69,6 +77,7 @@ export class CrossClusterReplicationServerPlugin implements Plugin; private readonly license: License; private readonly logger: Logger; + private ccrEsClient?: ICustomClusterClient; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); @@ -77,7 +86,7 @@ export class CrossClusterReplicationServerPlugin implements Plugin { + http.registerRouteHandlerContext('crossClusterReplication', async (ctx, request) => { + this.ccrEsClient = this.ccrEsClient ?? (await getCustomEsClient(getStartServices)); return { - client: ccrEsClient.asScoped(request), + client: this.ccrEsClient.asScoped(request), }; }); @@ -135,5 +142,10 @@ export class CrossClusterReplicationServerPlugin implements Plugin { private readonly logger: Logger; private readonly globalConfig$: Observable; private readonly license: License; + private rollupEsClient?: ICustomClusterClient; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); @@ -55,7 +63,7 @@ export class RollupPlugin implements Plugin { } public setup( - { http, uiSettings, elasticsearch }: CoreSetup, + { http, uiSettings, getStartServices }: CoreSetup, { licensing, indexManagement, visTypeTimeseries, usageCollection }: Dependencies ) { this.license.setup( @@ -72,12 +80,10 @@ export class RollupPlugin implements Plugin { } ); - // Extend the elasticsearchJs client with additional endpoints. - const esClientConfig = { plugins: [elasticsearchJsPlugin] }; - const rollupEsClient = elasticsearch.createClient('rollup', esClientConfig); - http.registerRouteHandlerContext('rollup', (context, request) => { + http.registerRouteHandlerContext('rollup', async (context, request) => { + this.rollupEsClient = this.rollupEsClient ?? (await getCustomEsClient(getStartServices)); return { - client: rollupEsClient.asScoped(request), + client: this.rollupEsClient.asScoped(request), }; }); @@ -116,7 +122,12 @@ export class RollupPlugin implements Plugin { const callWithRequestFactoryShim = ( elasticsearchServiceShim: CallWithRequestFactoryShim, request: KibanaRequest - ): APICaller => rollupEsClient.asScoped(request).callAsCurrentUser; + ): APICaller => { + return async (...args: Parameters) => { + this.rollupEsClient = this.rollupEsClient ?? (await getCustomEsClient(getStartServices)); + return await this.rollupEsClient.asScoped(request).callAsCurrentUser(...args); + }; + }; const { addSearchStrategy } = visTypeTimeseries; registerRollupSearchStrategy(callWithRequestFactoryShim, addSearchStrategy); @@ -140,5 +151,10 @@ export class RollupPlugin implements Plugin { } start() {} - stop() {} + + stop() { + if (this.rollupEsClient) { + this.rollupEsClient.close(); + } + } } diff --git a/x-pack/plugins/snapshot_restore/server/plugin.ts b/x-pack/plugins/snapshot_restore/server/plugin.ts index 00ff3db976d66..c5d3c665a3b7f 100644 --- a/x-pack/plugins/snapshot_restore/server/plugin.ts +++ b/x-pack/plugins/snapshot_restore/server/plugin.ts @@ -13,6 +13,7 @@ import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import { CoreSetup, + ICustomClusterClient, Plugin, Logger, PluginInitializerContext, @@ -31,10 +32,17 @@ export interface SnapshotRestoreContext { client: IScopedClusterClient; } +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + const esClientConfig = { plugins: [elasticsearchJsPlugin] }; + return core.elasticsearch.legacy.createClient('snapshotRestore', esClientConfig); +} + export class SnapshotRestoreServerPlugin implements Plugin { private readonly logger: Logger; private readonly apiRoutes: ApiRoutes; private readonly license: License; + private snapshotRestoreESClient?: ICustomClusterClient; constructor(private context: PluginInitializerContext) { const { logger } = this.context; @@ -44,7 +52,7 @@ export class SnapshotRestoreServerPlugin implements Plugin } public async setup( - { http, elasticsearch }: CoreSetup, + { http, getStartServices }: CoreSetup, { licensing, security, cloud }: Dependencies ): Promise { const pluginConfig = await this.context.config @@ -72,11 +80,11 @@ export class SnapshotRestoreServerPlugin implements Plugin } ); - const esClientConfig = { plugins: [elasticsearchJsPlugin] }; - const snapshotRestoreESClient = elasticsearch.createClient('snapshotRestore', esClientConfig); - http.registerRouteHandlerContext('snapshotRestore', (ctx, request) => { + http.registerRouteHandlerContext('snapshotRestore', async (ctx, request) => { + this.snapshotRestoreESClient = + this.snapshotRestoreESClient ?? (await getCustomEsClient(getStartServices)); return { - client: snapshotRestoreESClient.asScoped(request), + client: this.snapshotRestoreESClient.asScoped(request), }; }); @@ -95,11 +103,11 @@ export class SnapshotRestoreServerPlugin implements Plugin }); } - public start() { - this.logger.debug('Starting plugin'); - } + public start() {} public stop() { - this.logger.debug('Stopping plugin'); + if (this.snapshotRestoreESClient) { + this.snapshotRestoreESClient.close(); + } } } diff --git a/x-pack/plugins/transform/server/plugin.ts b/x-pack/plugins/transform/server/plugin.ts index 7da991bc02b37..c8057a3e2fae1 100644 --- a/x-pack/plugins/transform/server/plugin.ts +++ b/x-pack/plugins/transform/server/plugin.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, + ICustomClusterClient, Plugin, IScopedClusterClient, Logger, @@ -38,10 +39,18 @@ const PLUGIN = { }), }; +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + return core.elasticsearch.legacy.createClient('transform', { + plugins: [elasticsearchJsPlugin], + }); +} + export class TransformServerPlugin implements Plugin<{}, void, any, any> { private readonly apiRoutes: ApiRoutes; private readonly license: License; private readonly logger: Logger; + private transformESClient?: ICustomClusterClient; constructor(initContext: PluginInitializerContext) { this.logger = initContext.logger.get(); @@ -49,7 +58,7 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { this.license = new License(); } - setup({ elasticsearch, http }: CoreSetup, { licensing }: Dependencies): {} { + setup({ http, getStartServices }: CoreSetup, { licensing }: Dependencies): {} { const router = http.createRouter(); this.license.setup( @@ -72,12 +81,11 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { }); // Can access via new platform router's handler function 'context' parameter - context.transform.client - const transformClient = elasticsearch.createClient('transform', { - plugins: [elasticsearchJsPlugin], - }); - http.registerRouteHandlerContext('transform', (context, request) => { + http.registerRouteHandlerContext('transform', async (context, request) => { + this.transformESClient = + this.transformESClient ?? (await getCustomEsClient(getStartServices)); return { - dataClient: transformClient.asScoped(request), + dataClient: this.transformESClient.asScoped(request), }; }); @@ -85,5 +93,10 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { } start() {} - stop() {} + + stop() { + if (this.transformESClient) { + this.transformESClient.close(); + } + } } diff --git a/x-pack/plugins/watcher/server/plugin.ts b/x-pack/plugins/watcher/server/plugin.ts index 6a2e3b2e596b6..3f2891f919e37 100644 --- a/x-pack/plugins/watcher/server/plugin.ts +++ b/x-pack/plugins/watcher/server/plugin.ts @@ -12,6 +12,7 @@ declare module 'kibana/server' { import { CoreSetup, + ICustomClusterClient, IScopedClusterClient, Logger, Plugin, @@ -33,8 +34,15 @@ export interface WatcherContext { client: IScopedClusterClient; } +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + const esConfig = { plugins: [elasticsearchJsPlugin] }; + return core.elasticsearch.legacy.createClient('watcher', esConfig); +} + export class WatcherServerPlugin implements Plugin { - log: Logger; + private readonly log: Logger; + private watcherESClient?: ICustomClusterClient; private licenseStatus: LicenseStatus = { hasRequired: false, @@ -44,21 +52,17 @@ export class WatcherServerPlugin implements Plugin { this.log = ctx.logger.get(); } - async setup( - { http, elasticsearch: elasticsearchService }: CoreSetup, - { licensing }: Dependencies - ) { + async setup({ http, getStartServices }: CoreSetup, { licensing }: Dependencies) { const router = http.createRouter(); const routeDependencies: RouteDependencies = { router, getLicenseStatus: () => this.licenseStatus, }; - const config = { plugins: [elasticsearchJsPlugin] }; - const watcherESClient = elasticsearchService.createClient('watcher', config); - http.registerRouteHandlerContext('watcher', (ctx, request) => { + http.registerRouteHandlerContext('watcher', async (ctx, request) => { + this.watcherESClient = this.watcherESClient ?? (await getCustomEsClient(getStartServices)); return { - client: watcherESClient.asScoped(request), + client: this.watcherESClient.asScoped(request), }; }); @@ -89,6 +93,12 @@ export class WatcherServerPlugin implements Plugin { } }); } + start() {} - stop() {} + + stop() { + if (this.watcherESClient) { + this.watcherESClient.close(); + } + } } From 61936a1870977cb39d257a4964c86588cbb4416f Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Sat, 16 May 2020 09:08:45 +0200 Subject: [PATCH 12/14] [SOM] Preserve saved object references when saving the object (#66584) * create field for references and add comments * add FTR test * remove comments * address comments * use real reference in dataset and assert against it. --- .../public/lib/create_field_list.test.ts | 57 +++++++++++++++ .../public/lib/create_field_list.ts | 11 +-- .../object_view/components/field.tsx | 1 + .../object_view/components/form.tsx | 1 + .../edit_saved_object.ts | 69 +++++++++++++++++++ .../edit_saved_object/data.json | 9 ++- 6 files changed, 143 insertions(+), 5 deletions(-) diff --git a/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts index e7d6754ac4d05..7c447aefbc69a 100644 --- a/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts @@ -59,6 +59,11 @@ describe('createFieldList', () => { "type": "boolean", "value": true, }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, ] `); }); @@ -76,6 +81,11 @@ describe('createFieldList', () => { \\"data\\": \\"value\\" }", }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, ] `); }); @@ -93,6 +103,48 @@ describe('createFieldList', () => { 1, 2, 3 + ]", + }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, + ] + `); + }); + + it(`generates a field for the object's references`, () => { + const obj = createObject( + { + someString: 'foo', + }, + [ + { id: 'ref1', type: 'type', name: 'Ref 1' }, + { id: 'ref12', type: 'other-type', name: 'Ref 2' }, + ] + ); + expect(createFieldList(obj)).toMatchInlineSnapshot(` + Array [ + Object { + "name": "someString", + "type": "text", + "value": "foo", + }, + Object { + "name": "references", + "type": "array", + "value": "[ + { + \\"id\\": \\"ref1\\", + \\"type\\": \\"type\\", + \\"name\\": \\"Ref 1\\" + }, + { + \\"id\\": \\"ref12\\", + \\"type\\": \\"other-type\\", + \\"name\\": \\"Ref 2\\" + } ]", }, ] @@ -126,6 +178,11 @@ describe('createFieldList', () => { "type": "text", "value": "B", }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, ] `); }); diff --git a/src/plugins/saved_objects_management/public/lib/create_field_list.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.ts index 5d87c11a87198..d66d0b0a28844 100644 --- a/src/plugins/saved_objects_management/public/lib/create_field_list.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.ts @@ -29,12 +29,15 @@ export function createFieldList( object: SimpleSavedObject, service?: SavedObjectLoader ): ObjectField[] { - const fields = Object.entries(object.attributes as Record).reduce( + let fields = Object.entries(object.attributes as Record).reduce( (objFields, [key, value]) => { - return [...objFields, ...recursiveCreateFields(key, value)]; + return [...objFields, ...createFields(key, value)]; }, [] as ObjectField[] ); + // Special handling for references which isn't within "attributes" + fields = [...fields, ...createFields('references', object.references)]; + if (service && (service as any).Class) { addFieldsFromClass((service as any).Class, fields); } @@ -53,7 +56,7 @@ export function createFieldList( * @param {array} parents The parent keys to the field * @returns {array} */ -const recursiveCreateFields = (key: string, value: any, parents: string[] = []): ObjectField[] => { +const createFields = (key: string, value: any, parents: string[] = []): ObjectField[] => { const path = [...parents, key]; if (path.length > maxRecursiveIterations) { return []; @@ -78,7 +81,7 @@ const recursiveCreateFields = (key: string, value: any, parents: string[] = []): } else if (isPlainObject(field.value)) { let fields: ObjectField[] = []; forOwn(field.value, (childValue, childKey) => { - fields = [...fields, ...recursiveCreateFields(childKey as string, childValue, path)]; + fields = [...fields, ...createFields(childKey as string, childValue, path)]; }); return fields; } diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx index 1b69eb4240d68..afed6b492dc91 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx @@ -120,6 +120,7 @@ export class Field extends PureComponent { return (
{ set(source, field.name, value); }); + // we extract the `references` field that does not belong to attributes const { references, ...attributes } = source; await onSave({ attributes, references }); diff --git a/test/functional/apps/saved_objects_management/edit_saved_object.ts b/test/functional/apps/saved_objects_management/edit_saved_object.ts index 6af91ac9c5c94..1a85ff86498dc 100644 --- a/test/functional/apps/saved_objects_management/edit_saved_object.ts +++ b/test/functional/apps/saved_objects_management/edit_saved_object.ts @@ -26,6 +26,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common', 'settings']); + const browser = getService('browser'); + const find = getService('find'); const setFieldValue = async (fieldName: string, value: string) => { return testSubjects.setValue(`savedObjects-editField-${fieldName}`, value); @@ -35,6 +37,26 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { return testSubjects.getAttribute(`savedObjects-editField-${fieldName}`, 'value'); }; + const setAceEditorFieldValue = async (fieldName: string, fieldValue: string) => { + const editorId = `savedObjects-editField-${fieldName}-aceEditor`; + await find.clickByCssSelector(`#${editorId}`); + return browser.execute( + (editor: string, value: string) => { + return (window as any).ace.edit(editor).setValue(value); + }, + editorId, + fieldValue + ); + }; + + const getAceEditorFieldValue = async (fieldName: string) => { + const editorId = `savedObjects-editField-${fieldName}-aceEditor`; + await find.clickByCssSelector(`#${editorId}`); + return browser.execute((editor: string) => { + return (window as any).ace.edit(editor).getValue() as string; + }, editorId); + }; + const focusAndClickButton = async (buttonSubject: string) => { const button = await testSubjects.find(buttonSubject); await button.scrollIntoViewIfNecessary(); @@ -99,5 +121,52 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const objects = await PageObjects.settings.getSavedObjectsInTable(); expect(objects.includes('A Dashboard')).to.be(false); }); + + it('preserves the object references when saving', async () => { + const testVisualizationUrl = + '/management/kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed'; + const visualizationRefs = [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: 'logstash-*', + }, + ]; + + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + + const objects = await PageObjects.settings.getSavedObjectsInTable(); + expect(objects.includes('A Pie')).to.be(true); + + await PageObjects.common.navigateToActualUrl('kibana', testVisualizationUrl); + + await testSubjects.existOrFail('savedObjectEditSave'); + + let displayedReferencesValue = await getAceEditorFieldValue('references'); + + expect(JSON.parse(displayedReferencesValue)).to.eql(visualizationRefs); + + await focusAndClickButton('savedObjectEditSave'); + + await PageObjects.settings.getSavedObjectsInTable(); + + await PageObjects.common.navigateToActualUrl('kibana', testVisualizationUrl); + + // Parsing to avoid random keys ordering issues in raw string comparison + expect(JSON.parse(await getAceEditorFieldValue('references'))).to.eql(visualizationRefs); + + await setAceEditorFieldValue('references', JSON.stringify([], undefined, 2)); + + await focusAndClickButton('savedObjectEditSave'); + + await PageObjects.settings.getSavedObjectsInTable(); + + await PageObjects.common.navigateToActualUrl('kibana', testVisualizationUrl); + + displayedReferencesValue = await getAceEditorFieldValue('references'); + + expect(JSON.parse(displayedReferencesValue)).to.eql([]); + }); }); } diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json index f085bad4c507e..cbaa0306f9a42 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json @@ -38,7 +38,14 @@ }, "type": "visualization", "updated_at": "2019-01-22T19:32:31.206Z" - } + }, + "references" : [ + { + "name" : "kibanaSavedObjectMeta.searchSourceJSON.index", + "type" : "index-pattern", + "id" : "logstash-*" + } + ] } } From 10ff9a90d0edd09ff0710c46152811904c498c5a Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Fri, 15 May 2020 13:14:58 -0400 Subject: [PATCH 13/14] Skip failing lens test(s). #66779 --- x-pack/test/functional/apps/lens/smokescreen.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 082008bccddd1..5dd9364c48fda 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -72,7 +72,8 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { .perform(); } - describe('lens smokescreen tests', () => { + // Failing: https://github.com/elastic/kibana/issues/66779 + describe.skip('lens smokescreen tests', () => { it('should allow editing saved visualizations', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens'); From a997d1fbf5e3e2f948c6cb5439367b72103ed7df Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 18 May 2020 10:12:04 +0200 Subject: [PATCH 14/14] [ML] fix url assertion (#66850) --- .../apis/ml/job_validation/validate.ts | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/x-pack/test/api_integration/apis/ml/job_validation/validate.ts b/x-pack/test/api_integration/apis/ml/job_validation/validate.ts index aaeead57345bc..9695c2b6892e0 100644 --- a/x-pack/test/api_integration/apis/ml/job_validation/validate.ts +++ b/x-pack/test/api_integration/apis/ml/job_validation/validate.ts @@ -6,6 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../../functional/services/machine_learning/security_common'; +import pkg from '../../../../../../package.json'; const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', @@ -74,15 +75,14 @@ export default ({ getService }: FtrProviderContext) => { heading: 'Job ID format is valid', text: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, starts and ends with an alphanumeric character, and is no more than 64 characters long.', - url: - 'https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-job-resource.html#ml-job-resource', + url: `https://www.elastic.co/guide/en/elasticsearch/reference/${pkg.branch}/ml-job-resource.html#ml-job-resource`, status: 'success', }, { id: 'detectors_function_not_empty', heading: 'Detector functions', text: 'Presence of detector functions validated in all detectors.', - url: 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#detectors', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#detectors`, status: 'success', }, { @@ -90,8 +90,7 @@ export default ({ getService }: FtrProviderContext) => { bucketSpan: '15m', heading: 'Bucket span', text: 'Format of "15m" is valid and passed validation checks.', - url: - 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#bucket-span', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#bucket-span`, status: 'success', }, { @@ -104,8 +103,7 @@ export default ({ getService }: FtrProviderContext) => { id: 'success_mml', heading: 'Model memory limit', text: 'Valid and within the estimated model memory limit.', - url: - 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#model-memory-limits', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#model-memory-limits`, status: 'success', }, ]); @@ -157,15 +155,14 @@ export default ({ getService }: FtrProviderContext) => { id: 'job_id_invalid', text: 'Job ID is invalid. It can contain lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores and must start and end with an alphanumeric character.', - url: - 'https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-job-resource.html#ml-job-resource', + url: `https://www.elastic.co/guide/en/elasticsearch/reference/${pkg.branch}/ml-job-resource.html#ml-job-resource`, status: 'error', }, { id: 'detectors_function_not_empty', heading: 'Detector functions', text: 'Presence of detector functions validated in all detectors.', - url: 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#detectors', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#detectors`, status: 'success', }, { @@ -173,8 +170,7 @@ export default ({ getService }: FtrProviderContext) => { bucketSpan: '15m', heading: 'Bucket span', text: 'Format of "15m" is valid.', - url: - 'https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-job-resource.html#ml-analysisconfig', + url: `https://www.elastic.co/guide/en/elasticsearch/reference/${pkg.branch}/ml-job-resource.html#ml-analysisconfig`, status: 'success', }, { @@ -239,15 +235,14 @@ export default ({ getService }: FtrProviderContext) => { heading: 'Job ID format is valid', text: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, starts and ends with an alphanumeric character, and is no more than 64 characters long.', - url: - 'https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-job-resource.html#ml-job-resource', + url: `https://www.elastic.co/guide/en/elasticsearch/reference/${pkg.branch}/ml-job-resource.html#ml-job-resource`, status: 'success', }, { id: 'detectors_function_not_empty', heading: 'Detector functions', text: 'Presence of detector functions validated in all detectors.', - url: 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#detectors', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#detectors`, status: 'success', }, { @@ -262,8 +257,7 @@ export default ({ getService }: FtrProviderContext) => { fieldName: 'order_id', text: 'Cardinality of partition_field "order_id" is above 1000 and might result in high memory usage.', - url: - 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#cardinality', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#cardinality`, status: 'warning', }, { @@ -271,8 +265,7 @@ export default ({ getService }: FtrProviderContext) => { heading: 'Bucket span', text: 'Bucket span is 1 day or more. Be aware that days are considered as UTC days, not local days.', - url: - 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#bucket-span', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#bucket-span`, status: 'info', }, { @@ -287,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { { id: 'success_influencers', text: 'Influencer configuration passed the validation checks.', - url: 'https://www.elastic.co/guide/en/machine-learning/master/ml-influencers.html', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/ml-influencers.html`, status: 'success', }, { @@ -295,8 +288,7 @@ export default ({ getService }: FtrProviderContext) => { mml: '1MB', text: 'The specified model memory limit is less than half of the estimated model memory limit and will likely hit the hard limit.', - url: - 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#model-memory-limits', + url: `https://www.elastic.co/guide/en/machine-learning/${pkg.branch}/create-jobs.html#model-memory-limits`, status: 'warning', }, ]);